The State of Functional Reactive Programming in iOS: RxSwift vs. Combine | by Pierre-Yves Touzain | Mar, 2022

Source: undraw.co

First of all, we should start by explaining what is reactive programming. This is the first paragraph of the definition on Wikipedia:

In computing, reactive programming is a declarative programming paradigm concerned with data streams and the propagation of change. With this paradigm, it’s possible to express static (eg, arrays) or dynamic (eg, event emitters) data streams with ease, and also communicate that an inferred dependency within the associated execution model exists, which facilitates the automatic propagation of the changed data flow

The concept is simple: We have data emitters and data receivers, and in between, we apply operators to modify, transform, work on those data. Another way to say it is that it processes values ​​over time.

Those values ​​could be coming from network responses or user interface events. The concept of Reactive Programming is a direct replacement of completion blocks or delegates. This will result in a cleaner and more maintainable data pipeline. If used right, it can be a very powerful programming approach.

In Swift, the two main ways to do Reactive Programming are frameworks called Combine and RxSwift.

Apple heavily introduced SwiftUI during WWDC 2019, but what they barely said is that there is a whole framework that powers it: Combine. It’s a huge new framework that was too technical to make the headline, but it marks a big change in Apple’s philosophy, going from declarative to a functional reactive approach. You can use it starting iOS 13 and while it is very much associated with SwiftUI, you can still pair it with UIKit.

Several Foundation types expose their functionality through publishers, including Timer, NotificationCenterand URLSession. Combine also provides a built-in publisher for any property that’s compliant with Key-Value Observing.

Feel free to check Apple documentation about Combine for more overview.

RxSwift is an open-source framework. You can use it starting iOS 8. That’s a big advantage as you can safely start to integrate it into your project now while you may want to wait one year or two for Combine to support more iOS.

It’s a very popular framework and it has a big community around it so finding help and documentation is not difficult at all. You start by checking out the main GitHub repository where you’ll find all of their main documentation.

About Emitters

Combine: Publisher is a protocol that defines the requirements for a type to be able to transmit a sequence of values ​​over time to one or more subscribers. It’s kind of similar to what NotificationCenter is doing, except it’s of course done in a better and more readable way. You can create your own, but Apple also provides a whole set of pre-made publishers in its enum.

let publisher = NotificationCenter.default
.publisher(for: NSControl.textDidChangeNotification,
object: filterField)

RxSwift: They are called Observable. It’s a class that streams (not emit actually)Event which are then received by Observers. More on that later.

let name: String = "Peter"
let observable = Observable<String>.just(name)

About Receivers

Combine: Subscriber is a protocol that defines the requirements for a type to be able to receive input from a Publisher.

let subscriber = NotificationCenter.default
.publisher(for: NSControl.textDidChangeNotification,
object: filterField)
.sink(receiveCompletion: { print ($0) },
receiveValue: { print ($0) })

RxSwift: Observer direct subscribers to Observable. unlike NotificationCenterwhere developers typically use only its .default singleton instance, each observable in Rx is different. Also, it’s very important to know that an observable won’t stream events, or perform any work until it has a subscriber.

let name: String = "Peter"
let observable = Observable<String>.just(name)
observable.subscribe { event in
print(event)
}

Overall, you have a lot of operators in common with more or less the same name. Shai Mishali made this very handy spreadsheet to help you.

Back Pressure

Combine support back-pressure, but this is not the case with RxSwift. In ReactiveX, it is not difficult to get into a situation in which an Observable is streaming items more rapidly than an operator or observer can consume them. You still can control that with cold observables that stream a sequence when its observer finds it to be convenient and at whatever rate the observer desires, without disrupting the integrity of the sequence.

Error Type

Combine requires a specific error type while RxSwift is working with the simple Error type. It could sound like a more strideous task on the Combine side, but I think it is for the better. Error management is crucial and being “forced” to do your custom Error type will result in having better error logs.

Memory Management

RxSwift has a very handy system called Dispose Bag. All the subscriptions to observables are held by one unique object, which allows us to manage the life cycle of those subscriptions more effectively. This being said, subscriptions also work with ARC. A subscription is always dealloc when it’s completed or finished as an error.

let disposeBag = DisposeBag()let name: String = "Peter"
let observable = Observable<String>.just(name)
observable.subscribe { event in
print(event)
}.addDisposableTo(disposeBag)

For Combine, it’s called Cancellable. All subscriptions are managed separately. It’s a bit more effort to do but for a gain of more control over our code and possibly an improvement of performances as there are a lot of operations under the hood of DisposeBag

private var cancellable: AnyCancellable?self.cancellable = subject
.sink { value in
print(value)
}

Performances

Apple designed Combine with the performance aspect in mind. Quoting an Apple engineer: “The memory models of RxSwift and Combine are very different. Combine is really made for performance.” It nearly doubles the specs of RxSwift, which allows Apple to integrate Combine to high-performance demanding frameworks like RealityKit.

Comparison of similar methods execution between both frameworks (Graph by Stefan Kofler)

You have strong arguments on both sides to use one framework or another. Overall, use Combine if you want to use something natively integrated to iOS and if you don’t need to support iOS versions before iOS 13. It will also result in a gain in performance.

RxSwift on the other hand covers a wider range of iOS versions and also has a bigger community and more guides /examples /content around it.

But the important question is: Do you want to switch to Reactive Programming? It’s going to completely change your vision of data flow and architecture, to a modern, safer, and less error-prone approach, regarding the framework you choose.

Leave a Comment