Swift: How To Conform to the Sequence Protocol

I was testing out the SwiftyJSON library this weekend, and got the following error when I tried to iterate over my JSONValue object – an array of dictionaries:

Does Not Conform to Sequence Protocol

The issue is I forgot to call the array computed property on the my minionJSON value. This fixes the problem:

Array Object

However, the above solution left me unsatisfied. I wanted to be able to iterate over my JSON array the way I naturally did it initially. So I started researching how I can make SwiftyJSON’s JSONValue enum conform to the Sequence protocol. I found two really great blog posts on the subject, parts of which I’m still trying to fully understand if I’m truly honest:

So I set out to write my own extension for the SONValue enum that conforms to the Sequence protocol. The Sequence protocol looks very simple:

Sequence Protocol

Your class or enum just has to implement the generate() function that returns a Generator. If you click on the Generator in the documentation, you get the following protocol:

Generator Swift

So the object that conforms to the Generator protocol has to implement a next() function that returns an Element. Simple, right?! (this is sarcastic in case it doesn’t come through).

Thanks to the blog posts I mentioned above, and a lot of failure, I came up with the following Generator:

I check to see if the JSONValue is an array, keep track of where in the sequence I am in the global indexInSequence variable, and return the element in that position in my jsonArray. If the JSONValue is not an array or the indexOfSequence is out of bounds, nil is returned. Pretty simple in retrospect…

So now, the JSONValue just needs to implement the Sequence protocol’s generate() function:

Sequence Generate

That’s it! I can now iterate over my JSONValue like I originally wanted to:

Screen Shot 2014-07-22 at 8.55.25 AM

I’m still playing with Generators and Sequences, but it’s definitely something I’ll be considering implementing in my future classes and enums.

If you’d like to see my full sample project, the source code is on Github here.

Enjoy the article? Join over 14,500+ Swift developers and enthusiasts who get my weekly updates.

  • Peter J. Kootsookos

    Nice writeup!

    Just wondering why indexInSequence needs to be global?

    (Insert bad ju-ju about globals here).

  • Ivan Schuetz

    First of all thanks for this example, it’s very helpful and easy to understand. A small criticism though, about this particular use case – I don’t think it’s a good a practice to make JSONValue, which can be a lot of things that are not sequenceable, like scalar values, extend Sequence. I think extension should comply to “is a” relationship and JSONValue is not a sequence. It’s only one when it’s an array, because of which I would prefer to iterate over the array like in your first example.

  • Mark Probst

    It’s a bad idea to make indexInSequence global. It won’t allow you to nest loops, nor will you be very happy if your code is multithreaded. Just put the variable in the generator.

  • Hey! Great post. Do you have any idea if there are changes in the public release? My playground won’t accept “Generator” and “Sequence” as valid types. There are “SequenceType” and “GeneratorType” – but I can’t make them work with your example! Any help appreciated.

    • Actually, got it to work finally. So it seems that Generator and Sequence has been replaced with GeneratorType and SequenceType. I was trying to create a “Bag” implementation in Swift, that implements the generator. Here’s my gist: https://gist.github.com/testower/fa1ba7727735ddc6331c

      • Did you know you can also avoid declaring the JSONArrayGenerator struct? In generate() you can use GeneratorOf with a closure that contains your next-implementation. See the gist i posted for an example.

  • rayascott

    The code inside the if clause of the .JArray case in next() can be reduced to a single line:

    return jsonArray[indexInSequence++]

    It’s a postfix ++, don’t forget.

  • AstrayPharaoh

    Regarding your implementation of next(), is really necessary to set indexInSequence to 0 when there’s no elements left? I because because structs are a value type, and it is my understanding a copy is passed to the for-in itself, so your original object shouldn’t have a changed state.