Swift Async Sequence extensions (part 2)
In Swift Async Sequence extensions (part 1) I covered simple ways to create an async sequence using a custom factory method and binding a sequence to a UI control via a custom assign method.
This covered the “beginning” and “end” of the data stream (so to say) but what about processing or converting the data along the way? If you draw a parallel to Combine code — how would you build custom “operators” for your async sequence?
In this post I’ll show you exactly that — building a method that converts one async sequence into another.
Existing AsyncSequence methods
Plenty of the new Swift Concurrency code is freely available at Apple’s GitHub so should you want to peak in you can.
For example here’s the AsyncSequence.filter(_:) source code:
- It defines an extension on
AsyncSequence - Adds the
filtermethod - And finally declares a type called
AsyncFilterSequence— this is what thefiltermethod returns.
This is very similar setup to what the Combine operators do — most often they are a method on the Publisher protocol and return some kind of custom publisher that implements the operator logic. So no surprises here.
If you want inspiration for build your own async sequence methods you can check out map, prefix, and so on.
Note: The whole repo is very interesting to read through. For example have a look at how
Task.sleep(...)looks like.
Building a custom async sequence method
For this article I’ll build an alternative of the Publisher.replaceNil(_:) method from Combine. It’s a handy operator that replaces any nil values in the stream with a given constant.
Following the pattern established in the files I linked above, I’ll start by creating a custom async sequence type called AsyncReplaceNilSequence:
|
|
This new sequence AsyncReplaceNilSequence is generic (T) over its elements. An additional where clause requires that the base sequence has optional values (T?). This way if the base sequence contains, for example, elements String? — the resulting new sequence will have all nil values replaced and the output elements will be of type String.
Next, I need a proper initializer that will take the base sequence and the constant to replace nil values with:
|
|
The property base holds onto the “source” sequence (or the base sequence) and replaceValue is a constant to replace nil values with.
The final step in building the sequence is to define the sequence iterator. First I’ll add the sequence method that creates the iterator:
|
|
And then I’ll add Iterator as a nested structure inside AsyncReplaceNilSequence:
|
|
Very similarly to the sequence type itself, the iterator also holds on to its base counterpart and the value to use for replacing.
The meaningful step here is defining the iterator next() method that will pull values from the base sequence and replay them while replacing any nil values:
|
|
The method fetches the next() value from its base iterator and in case the result is nil it returns the replace constant instead.
So now I have an async sequence and a working iterator. I only need the method on AsyncSequence before I wrap up. I chose to mimic the method signature of Combine’s replaceNil() like so:
|
|
The method is generic over its parameter type and that allows it to accept a String parameter if the sequence is String? or an Int parameter if the sequence is Int?, and not be available at all if the sequence is not of an optional element type.
Trying the code in a view
Let’s give the code a try. I’ll reuse my code from part 1 including my Array.spread() and AsyncSequence.assign() methods that create a sequence of values over time and bind them to a label on screen.
I’ll use an [Int?] array to display some numbers on screen:
|
|
I’m using map to convert the optional number to a string before binding it to the @State property. That displays the description of the optional instead of the value by default:
And this is one of the common use cases that replaceNil(_:) is good for — if you have a fallback value for nil you can unwrap the sequence by removing the optional from the generic and bind the sequence directly like so:
|
|
And that’s all for today! I this code and spelling out the steps was useful — you can find the complete source code below.
The completed source code
This is a whole bunch of code but including it below in case you’d like to copy it over and play with it:
|
|
One final note — when you’re building a method like this for production it makes sense to optimize for performance and add inline-able decorators. If you want to get some insight on how Apple’s done that have a look at AsyncSequence.map’s source code.
Where to go from here?
If you’d like to support me, get my book on Swift Concurrency:
Interested in discussing the new async/await Swift syntax and concurrency? Hit me up on twitter at https://twitter.com/icanzilb.

