featherless software design

View on GitHub
9 October 2015

Enumerate Midipacketlist In Swift Part 2

by

Enumerate MIDIPacketList in Swift: Part 2

This is the conclusion of Enumerate MIDIPacketList in Swift.

In Part 1 we examined the structure of MIDIPacketList and built an Objective-C-influenced implementation of SequenceType. In this post we’ll look at Swift 2’s AnyGenerator type to achieve the same effect — but more concisely — thanks to Swift type inference and closures.

AnyGenerator

Swift 2’s AnyGenerator type - and corresponding anyGenerator method - allow us to create inline generators. This removes the need to create a separate Generator class and — paired with type inference — allows us to remove a lot of boilerplate. Let’s re-examine our MIDIPacketList extension.

The essential compilable code for a SequenceType built with AnyGenerator looks like this:

extension MIDIPacketList: SequenceType {
  public func generate() -> AnyGenerator<MIDIPacket> {
    // TODO: Local state...

    return anyGenerator({
      // TODO: return nil if no additional elements

      // TODO: Iterator logic

      return nil // TODO: return the next sequence item
    })
  }
}

The Generator in the code above will not generate anything. Let’s fill in the implementation.

Step 1: Iterator state

First we define the generator’s iterator.

public func generate() -> AnyGenerator<MIDIPacket> {
  var iterator: MIDIPacket?
  var nextIndex: UInt32 = 0

Step 2: Iterator logic

Next we define the logic that will advance the iterator on each call of the generator.

  return anyGenerator {
    if iterator == nil {
      iterator = self.packet
    } else {
      iterator = withUnsafePointer(&iterator!) { MIDIPacketNext($0).memory }
    }
    return iterator
  }

Step 3: Iterator termination

The final implementation advances and checks nextIndex against self.numPackets before iterating in order to avoid accessing invalid memory.

public func generate() -> AnyGenerator<MIDIPacket> {
  var iterator: MIDIPacket?
  var nextIndex: UInt32 = 0

  return anyGenerator {
    if nextIndex++ >= self.numPackets { return nil }
    if iterator == nil {
      iterator = self.packet
    } else {
      iterator = withUnsafePointer(&iterator!) { MIDIPacketNext($0).memory }
    }
    return iterator

  }
}

Addendum

This series went through multiple iterations as I learned more Swift language features. Check out the revision history of this entry’s code.

tags: