Learn & Master ⚔️ the Basics of Combine in 5 Minutes

Sebastian Boldt
iOS App Development
5 min readJul 9, 2019

Because RxSwift and Combine have so much in common I thought it would be a great idea to adapt the things I know about RxSwift and use them to create something new.

This is an article based on one of my most successful articles here on Medium (Learn and Master Rx-Swift). More than 140.000 Impressions so far. That’s Amazing, thank you so much.

So lets get started with Apples take on reactive programming: COMBINE

1. Publishers

The first thing you need to understand is that everything in Combine is a Publisher or something that operates on or subscribes to values emitted by a Publisher.

Arrays, Strings or Dictionaries can be converted to Publishers in Combine.
Let’s create some simple ones.

let helloPublisher = "Hello Combine".publisher()
let fibonacciPublisher = [0,1,1,2,3,5].publisher()
let dictPublisher = [1:"Hello",2:"World"].publisher()

You subscribe to publishers by calling
sink(receiveValue: (value -> Void))
The passed block will receive all values emitted by that publisher.

let fibonacciPublisher = [0,1,1,2,3,5].publisher()_ = fibonacciPublisher.sink { value in
print(value)
}
OUTPUT:
0 1 1 2 3 5

Publishers can emit zero or more values over their lifetimes.
Besides the basic values your Publisher also emits special values represented by the Subscribers.Completion enum.

  • .finished will be emitted if the subscription is finished
  • .failure(_) will be emitted if something went wrong

The associated value for the failure case can be a custom Object, an Error or a special Never object that indicates that the Publisher won’t fail.

let fibonacciPublisher = [0,1,1,2,3,5].publisher()
_ = fibonacciPublisher.sink(receiveCompletion: { completion in
switch
completion {
case .finished:
print("finished")
case .failure(let never):
print(never)
}
}, receiveValue: { value in
print(value)
})
OUTPUT:
0 1 1 2 3 5
finished

If you want to cancel a subscription you can do that by calling cancel on it.

let subscriber = fibonacciPublisher.sink { value in
print(value)
}
subscriber.cancel()

2. Subjects 📫

A Subject is a special form of a Publisher, you can subscribe and dynamically add elements to it. There are currently 2 different kinds of Subjects in Combine

  • PassthroughSubject: If you subscribe to it you will get all the events that will happen after you subscribed.
  • CurrentValueSubject: will give any subscriber the most recent element and everything that is emitted by that sequence after the subscription happened.

PassthroughSubject

The first thing we need to do is to create an actual PassthroughSubject instance. This is super easy, we can use the default initializer for that.

let passthroughObject = PassthroughSubject<String,Error>()

You can add new Values to that subject by using the send(input:String) function. If you want to complete the sequence you need to use send(completion: .finished).

send(completion: someError) will emit an error.
Let’s add some values to our PublishSubject.

passthroughObject.send("Hello")
passthroughObject.send("World")

If you subscribe to that subject after adding “Hello” and “World” using send(), you won’t receive these two values. You will just receive values that where sent after you subscribed to the Subject

let passThroughSubject = PassthroughSubject<String, Error>()
passThroughSubject.send("Hello")
passThroughSubject.send("World")
passThroughSubject.sink(receiveValue: { value in
print(value)
})
OUTPUT:
NO OUTPUT

Now we send a value after we subscribed and vola. The value will be printed.

let passThroughSubject = PassthroughSubject<String, Error>()
passThroughSubject.send("Hello")
passThroughSubject.sink(receiveValue: { value in
print(value)
})
passThroughSubject.send("World")OUTPUT:
World

CurrentValueSubject

In contrast to a PassthroughSubject, the CurrentValueSubject will receive the “World”-String, which is the most recent value.
So it works a little bit more like an actual variable.

let subject = CurrentValueSubject<String, Error>("Initial Value")
subject.send("Hello")
subject.send("World")
currentValueSubject.sink(receiveValue: { value in
print(value)
})
OUTPUT:
World

Congratulations 🎉. If you kept up reading to this point you should know the basics of Combine. There is a lot more to learn, but everything around Combine is based on these simple principles. You can take a short break now and play around with these concepts to fully understand them. If you are ready let us continue because there is a lot more interesting stuff to uncover.

3. Mapping, Transforming, Filtering ⚙️

It is also possible to transform or filter the elements emitted by a Publisher before the subscriber receives them.

3.1 Map

To transform Elements emitted from a Publisher to something else, before they reach their subscribers, you use the map operator.

Imagine a transformation that multiplies each value of a sequence with 10 before emitting

[1,2,3,4].publisher().map {
return $0 * 10
}.sink { value in
print(value)
}
OUTPUT: 10 20 30 40

3.2 Scan

Scan starts with an initial seed value and is used to aggregate values just like reduce in Swift.

[1,2,3,4,5].publisher().scan(0) { seed, value in
return
seed + value
}.sink { value in
print(value)
}
OUTPUT: 1 3 6 10 15

3.3 Filter

To Filter elements you just define a condition that needs to be passed and if the condition is fulfilled the value will be emitted to its subscribers.

[2,30,22,5,60,1].publisher().filter{
$0 > 10
}.sink { value in
print(value)
}
OUTPUT: 30 22 60

Other filter operators you should try:

  • flatMap
  • replaceEmpty
  • max & min

4. Combine & SwiftUI/UIKit

Combine and SwiftUI or UIKit can be used to create amazing things like reactive-ui based on object-binding

UIKit-Example:
A ViewModel-Property directly attached to its Control

import Combine
import UIKit
struct ViewModel {
@Published var switchState: Bool = false
}
final class SomeViewController: UIViewController {
private var subscriber: AnyCancellable?
private var viewModel = ViewModel()

@IBOutlet private weak var aSwitch: UISwitch!
override func viewDidLoad() {
super.viewDidLoad()
let queue = DispatchQueue.main
subscriber = publisher.assign(to: \.isEnabled,
on: aSwitch)
}

@IBAction func didSwitch(_ sender: UISwitch) {
viewModel.switchState = sender.isOn
}
}

It’s a wrap 🎁

Congratulation, you learned the basics of COMBINE. Happy Coding 🎉

If the article has helped you and you want me to continue writing similar articles, you are welcome to support me with a small donation

🤜🏾 🤛

Feel free to add me on github, twitter, linkedin or xing if you have any questions. If you like electronic music you can also listen to my Tracks on SoundCloud ;)

--

--

Sebastian Boldt
iOS App Development

Creative Head, iOS Developer @ Immowelt, DJ/Producer from Hamburg. Creator of HabitBe, a weekly Habit Tracker written in SwiftUI. https://www.sebastianboldt.com