Introduction to Combine for iOS developers with RxSwift experience

Everybody who used RxSwift or in general reactive programming concepts in their projects knows it’s advantages. Doing async work in a unified way, composing asynchronious operations, avoiding callback hell, writing more clean and readable code are some of them.

Apple also recognized the benefits of reactive programming and introduced their new Combine framework at WWDC 2019. Although Apple is not using the word reactive programming in their official documentation, Combine can be described as a reactive framework. It offers iOS developers a unified declarative Swift API for processing values over time.

If you are new to reactive programming, here is a list of books available on Amazon to get you started:

Key concepts of Combine

The key concepts of Combine are really similar to those of RxSwift. The namings are different though. So the first thing RxSwift developers will need to do is to get used to these new namings.

Oh my, so many namings! Let’s relax first.

Publisher & Subscriber

A Publisher is what in RxSwift is an Observable. A publisher defines how values are produced and allows registration of a subscriber.

A Subscriber is what in RxSwift is an Observer. A subscriber receives values from the publisher after subscribing.

Let’s remind how to create and subscribe to an Observable in RxSwift first:

let observable = Observable.from([1, 2, 3, 4])
observable.subscribe(onNext: { value in
    print(value)
}, onCompleted: {
    print("completed")
})

And here how it looks to create and register to a Publisher in Combine:

let publisher = Publishers.Sequence<[Int], Error>(sequence: [1, 2, 3, 4])
publisher.sink(receiveCompletion: { _ in
    print("completed")
}) { value in
    print(value)
}

Both examples produce the same output:

1
2
3
4
completed

SubjectType

A SubjectType is what in RxSwift is a Subject, so the name is not that different in this case. Only the subtypes of a subject were named differently.

There are two kinds of a subject type in Combine: PassthroughSubject and CurrentValueSubject.

A PassthroughSubject is what in RxSwift is a PublishSubject. If you subscribe to it you will get all events that happen after you subscribed.

A CurrentValueSubject is what in RxSwift is a BehaviourSubject. It behaves like a PassthroughSubject but it also will send it’s most recent element to new subscribers.

Let’s remind how to create a subject and sending values to the observer in RxSwift:

let subject = PublishSubject<String>()
subject.asObservable().subscribe(onNext: { value in
    print(value)
})

subject.onNext("Yes")
subject.onNext("Ve")
subject.onNext("gan")

And here the equivalent code in Combine:

let subject = PassthroughSubject<String, Never>()
subject.eraseToAnyPublisher().sink { value in
    print(value)
}

subject.send("Yes")
subject.send("Ve")
subject.send("gan")

Both examples produce the same output:

Yes
Ve
gan

If you like going deeper, you can find a good comparison of available RxSwift und Combine components in this article.

Operators

Many operators like map, flatMap, reduce etc. that are available in RxSwift are also available in Combine. Using Combine operators is just like using RxSwift operators.

So it should feel really familiar to see Combine operators in action:

Publishers.Sequence(sequence: [1, 2, 2, 3, 3, 4, 7])
    .map { $0 * 2 }
    .filter { $0.isMultiple(of: 2) }
    .dropFirst(3)
    .removeDuplicates()
    .sink { value in
        print(value)
}

This code produces the following output:

6
8
14

A full comparison list of RxSwift und Combine operators is available in this article.

Error Handling

A notable difference between RxSwift’s Observable and Combine’s Publisher is the error handling. In RxSwift, you always work with the general Error type, whereas in Combine you can use any kind of custom error. So when creating a publisher or a subject, the error type needs to be defined:

let publisher = Publishers.Sequence<[Int], SomeCustomError>(sequence: [1, 2])

Of course, you can also use the general Error type here. If there will be no error, you can use Never:

let subject = PassthroughSubject<String, Never>()

Memory Management

To deal with ARC and memory management, RxSwift provides a DisposeBag. In Combine you can use a collection of AnyCancellables.

Here is an example of how a DisposeBag is typically used in RxSwift:

let disposeBag = DisposeBag()

let subject = PublishSubject<String>()
subject.asObservable().subscribe(onNext: { value in
    print(value)
}).disposed(by: self.disposeBag)

And here is the equivalent example in Combine:

var cancellables = Set<AnyCancellable>()

let subject = PassthroughSubject<String, Never>()
subject.eraseToAnyPublisher().sink { value in
    print(value)
}.store(in: &cancellables)

Performance

I didn’t compare the performance of RxSwift and Combine myself but there are some great articles about it. They all agree that Combine is clearly faster than RxSwift.

If you are interested in more specifics, you can find some performance tests and it’s results here. The tests compare the performance of the most commonly used components and operators in RxSwift and Combine.

Conclusion

RxSwift is great and it’s kind of sad by seeing it being replaced. But it is also great that reactive programming is now being pushed and supported by Apple.

There are some strong arguments for switching to Combine. Besides the fact of having one external dependency less, the performance lead is not neglectable. Combine also works greatly with SwiftUI, another new framework introduced at WWDC 2019.

The only reason, you might not want to switch to Combine right away is it’s backward compatibility. Combine is supported only for iOS 13+. So if you need to support lower iOS versions, you may want to wait.


Did you like this post? Then let me know by clicking on the like button below.

Like to stay updated? You can follow me on Twitter or subscribe to this blog. If you like to read on Medium, I’m also publishing my posts there. Feel free to contact me if you have any questions or want to give me some feedback.

5+