woman looking at the map

Convert array types using the map operator in Combine

In reactive programming, it’s very common to require a stream of values in the form of an array. A typical use case for this is to populate a list with data. However, the array that is published from the source may need to be transformed to an array of values of a different type for display.

Let’s look at an example stream which begins with an array of Entity values. Our goal is to transform each value in the array of Entity, into a new value of EntityDisplay which is more suitable for rendering in the UI.

struct Entity {
    let id: String
    let name: String
    let description: String
    let date: Date
}
struct DisplayEntity {
    let entity: Entity
    
    var text: String  {
        return "\(entity.name): \(entity.description)"
    }
    
    var date: String {
        let formatter = DateFormatter()
        formatter.dateStyle = .short
        formatter.timeStyle = .short
        return formatter.string(from: entity.date)
    }
}

We’ll use a helper function to create some entities to work with:

func createEntities() -> [Entity] {
    return (0..<5).map {
        let id = $0 + 1
        return Entity(id: "\(id)", name: "Entity \(id)", description: "Description \(id)", date: Date())
    }
}

Once we have a publisher for our array of Entity values, mapping to an array of EntityDisplay values requires 2 simple steps.

  • Use the Combine map operator to transform the [Entity] publisher to a publisher of a new value (in our case this will be [EntityDisplay]).
  • Inside of this transform closure, we use the standard Swift Collection map method on the [Entity] array itself, mapping each value from Entity to DisplayEntity.
Just(createEntities())
    .map { $0.map { DisplayEntity(entity: $0) } }
    .sink { print($0) }
    .store(in: &subscriptions)

It’s as simple as that!

We can take this a step further, by creating an extension on Publisher to encapsulate this ‘2 step’ mapping. Shout out to @danielt1263 on the RxSwift slack group for this great snippet below.

extension Publisher where Output: Sequence {
    public func mapArray<Input, Out>(_ transform: @escaping (Input) -> Out) -> Publishers.Map<Self, [Out]> where Output.Element == Input {
        map { $0.map { transform($0) } }
    }
}

Using this extension, we can make our mapping look a little more zen, like so:

Just(createEntities())
    .mapArray { DisplayEntity(entity: $0) }
    .sink { print($0) }
    .store(in: &subscriptions)

You can grab all of the code above from this gist. There will be many more posts on reactive programming in Combine coming soon, so stay tuned by subscribing below 🚀.

Stay in the Loop

Subscribe to tapdev and never miss a post.

Total
0
Shares
Previous Post
facades of buildings in modern town

Neatly organise complex views with a UIView subclass

Next Post
adult blur books close up

Is it worth learning RxSwift in 2021?

Related Posts