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

Sebastian Boldt
Jul 9 · 5 min read

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

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.

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 = PassthroughObject<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

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
}
}

SwitftUI Example:
A list of objects that is directly connected to a List.

import SwiftUI
import Combine
struct ContentView : View {
@ObjectBinding var dataProvider: DataProvider
var body: some View {
List(dataProvider.items) {
Text($0)
}
}
}
class DataProvider: BindableObject {
var didChange = PassthroughSubject<Void, Never>()
private(set) var items: [String] {
didSet {
didChange.send()
}
}
init(items: [String]) {
self.items = items
}
private func requestData() {
// This function will request data from the server
....
items.append(item)
}
}
extension String: Identifiable {
public var id: Int {
return self.hashValue
}
}

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 ;)

iOS App Development

Stories and technical tips about building apps for iOS, Apple Watch, and iPad/iPhone

Sebastian Boldt

Written by

Creative Head, iOS Developer @ Immowelt, DJ/Producer from Hamburg. Creator of Jelly, an animation Framework written in Swift. https://www.sebastianboldt.com

iOS App Development

Stories and technical tips about building apps for iOS, Apple Watch, and iPad/iPhone

Welcome to a place where words matter. On Medium, smart voices and original ideas take center stage - with no ads in sight. Watch
Follow all the topics you care about, and we’ll deliver the best stories for you to your homepage and inbox. Explore
Get unlimited access to the best stories on Medium — and support writers while you’re at it. Just $5/month. Upgrade