My struggles against learning Apple’s Combine framework on my own I thought it will be a nice way of learning by trying to build my own operator.
So when I stumbled across a common use case that I can try building an operator for, I challenged myself to do so.
The common use case in hand
Let’s say for example that you have an api service that returns an array of objects (object A) and this Codable object is full of data that we don’t really want to use, perhaps we just want to display on the screen a part of that data or even more than that, we would like to manipulate the data and format it in some way so it suites our view perfectly.
In order to do that we will need a second type of object (object B) and do some sort of mapping between object A and object B (transform object A to object B) so that we get an array of type object B.
So, if we have a publisher of array of type object A, our mission is to transform every item in the array into object B and end up with a publisher of array of type object B.
So how we can do that you ask? great question. Let’s find out :)
Preparing the playground
NOTE: Feel free to open up playground and start experimenting yourself.
For the sake of simplicity let’s use the following structs:
Now, let’s create a simple users list of type UserDataModel:
Before we continue, let’s create a computed variable inside UserDataModel struct that will allow us easily convert a UserDataModel object into a User object like this:
Recalling our mission here, mapping the objects can be achieved by using the Combine’s map operator (outer map) to convert it to another list of objects and using Swift standard library’s map function (inner map) to iterate over the list and convert each UserDataModel element to User.
While this approach works fine, it does not read very well and it can be pretty confusing to use.
Let’s see how we can take things one step further and create an extension on Publisher and use some pretty basic operators on that publisher to achieve the same result but in a way nicer way.
In simple words, we will do the folowing steps:
- Take the given array of UserDataModel objects (upstream value) and break it down to separated UserDataModel values.
- Convert each of the elements into a User object.
- Assemble back an array of User objects (downstream value).
Quick exercise: Try to guess what are the actual operators described above.
Custom Combine Operator: MapEach
Let’s watch the magic written in code:
NOTE: These two functions do the same with different parameters (closure and keypath).
We extend Publisher only where the Output type conforms to Sequence and Failure is Never because this is actually our use case. These constraints help us along the way. Also, the downstream array’s object type is generic.
First, we apply the flatMap operator to transform the array from the upstream publisher into a new publisher, in this case, a publisher of sequence.
Next, we apply the map operator on each element, either using a closure or a keypath in order to get the converted elements stream.
Finally, we apply the collect operator to assemble all the elements back together into an array.
Let’s see it in action:
I hope this article has shed some light on how you can build your own Combine operators and that it will give you the strength to continue exploring this amazing framework.
I would like to take this opportunity to thank Ruslan Sharaev¹ for giving me the idea of that kind of use case which I simply took as a challenge to make from it a useful operator that we can all use.
: Ruslan Sharaev: 4 Combine and Swift UI Example App Swift Issues (https://www.youtube.com/watch?v=e8TA6hc6AFI)