Recently, our iOS team has prepared a report on the Combine Framework. We’ve discussed using the Combine framework, announced by Apple in 2019, while working with asynchronous code and how to use the delegate-pattern and callbacks as an alternative.
We’ve covered the following:
▪️ The comparison of Combine with other reactive paradigms, such as Rx and ReactiveSwift
▪️ Such entities as Publisher, Subscriber, Subject, Subscription, Scheduler
Lifecycle subscription and Backpressure work
▪️ Operators work
▪️ Debugging of the Reactive approach
▪️ Error handling
▪️ Usage of Combine with Foundation
▪️ Practical usage of Combine in typical situations
The report will be useful to those who:
▪️ Wants to stop using third-party dependencies such as Rx and ReactiveSwift
▪️ Wants to follow a declarative approach to programming in the future, but with a native framework.
2. 2
Combine
A unified declarative API for processing values over time
SwiftUI:
1. @State
2. @Binding
3. @ObservedObject
Foundation:
1. Timer
2. NotificationCenter
3. URLSession
4. KVO
Core Data:
1. FetchedRequest
2. NSManagedObject
3. 3
ReactiveSwift vs Combine
Rx ReactiveSwift Combine
Deployment target iOS 8+ iOS 8+ iOS 13+
Platform supported macOS, iOS, watchOS, tvOS, and
Linux
macOS, iOS, watchOS, tvOS, and
Linux.
macOS, iOS, watchOS, tvOS, and
UIKit for Mac.
Framework consumption Third-party Third-party First-party
Maintained by Open source Open source Apple
UI bind RxCocoa ReactiveCocoa SwiftUI
passwordTextField.rx.text.orEmpt
y .bind(to: viewModel.password)
.disposed(by: disposeBag)
testLabel.reactive.text <~
signal.take(duringLifetimeOf:
self)
publisher.assign(to: .text, on:
label)
Error Management
- + +
Backpressure
- - +
4. 4
ReactiveSwift vs Combine
Combine ReactiveSwift
Publisher* Signal, SignalProducer
Subscriber* Observer
Value event:
value(Output)
Completion:
finished
failure(Error)
Value event:
value(Output)
Completion:
completed
failed(Error)
interrupted
5. 5
Publishers
Emit values over time to one or more interested parties, such as subscribers.
Including operation: math calculations, networking or handling user events, every publisher can emit multiple events
types:
1. An output value of the publisher's generic Output type.
2. A successful completion.
3. A completion with an error of the publisher's Failure type.
A publisher can emitzero or morevalues but onlyone completion event, which can either be a normal completion event or an
error.
Once a publisher emits a completion event, it’s finished and can no longer emit any more events.
A publisher only emits an event when there’s at least one subscriber.
Summary:
1. describe how values and errors are produced.
2. value type
3. allow registration of a Subscriber
4. don’t emit values or perform work if they don’t have any subscribers (exeption Future)
7. 7
Publishers
Combine provides a number of additional convenience publishers:
● Just
● Empty
● De!ered
● Future
● Sequence
● ObservebleObjectPublisher
● @Publisher
● Fail
Just- a publisher that emits a single value to a subscriber and then complete.
Future- asynchronouslyproduce a single result and then complete. Executes immediately when it is created instead of waiting for a subsc
like a normal publisher does. They’re working with a Future that begins its execution immediately, emits a single value and re-emits the
value to its subscribers if it receives more than a single subscriber. Reference type.
De!eredallows to change from Future<Int, Never> to Deferred<Future<Int, Never>>. And than we have:
● The publisher will not perform work until it has subscribers
● If you subscribe to the same instance of this publisher more than once, the work will be repeated
Emptypublisher type can be used to create a publisher that immediately emits a .finished completion event.
8. Combine vs ReactiveSwift
Publisher
AnyPublisher
1. +
2. +
3. +
4. +
Future
1+, 2-, 3+, 4+, 5-
PassthroughSubject. / CurrentValueSubject
1-, 2+, 3-, 4+, 5+
SignalProducer
1. Signal producers start work on demand by
creating signals
2. Each produced signal may send di!erent
events at di!erent times
3. Disposing of a produced signal will interrupt it
4. Need to have a least one observer
Signal
1. Signals start work when instantiated
2. Observing a signal does not have side e!ects
3. All observers of a signal see the same events
in the same order
4. Terminating events dispose of signal
resources
5. Has manually controlled signal
(let (signal, observer) = Signal<Int, Never>.pipe())
8
10. Subscribers
10
Responsible for requesting data and accepting the data (and possible failures) provided by a publisher.
Initiates the request for data, and controls the amount of data it receives.
Described with two associated types, one for Input and one for Failure.
Summary:
● receives values and a completion
● reference type
16. Subjects
16
Special case of publisher that also adhere to the subject protocol.
This protocol requires subjects to have a.send() method to allow the developer to send specific values
to a subscriber (or pipeline).
Subjects can be used to "inject" values into a stream, by calling the subject’s .send() method.
A subject will not signal for demand to its connected publishers until it has received at least one subscriber.
itself. When it receives any demand, it then signals for unlimited demand to connected publishers.
There are two types:
1. CurrentValueSubject
2. PassthroughSubject.
19. Life cycle
19
● When the subscriber is attached to a publisher, it starts with a call to.subscribe(Subscriber).
● The publisher in turn acknowledges the subscription callingreceive(subscription).
● After the subscription has been acknowledged, the subscriber requestsNvalues withrequest(_ : Demand).
● The publisher may then (as it has values) sendingN(or fewer)values: receive(_ : Input). A publisher should never sendmore
than the demand requested.
Also after the subscription has been
acknowledged, the subscriber can send
cancellation with.cancel().A publisher may
optionally sendcompletion:
receive(completion:).A completion can be
either a normal termination, or may be a
.failure completion, optionally propogat
an error type.
A pipeline that has been cancelled will n
send any completions.
20. Operators
20
Combine provides two built-in subscribers, which make working with data streams straightforward:
1. Thesinksubscriber allows you to provide closures with your code that will receive output values and com
From there, you can do anything your heart desires with the received events.
2. Theassignsubscriber allows you to, without the need of custom code,bindthe resulting output to some property
on your data model or on a UI control to display the data directly on-screen via a key path.
21. Operators
21
An object that acts both like a subscriber and a publisher.
Operators are classes that adopt both the Subscriber protocol and Publisher protocol.
They support subscribing to a publisher, and sending results to any subscribers.
You can create chains of these together, for processing, reacting, and transforming the data provided by a publish
requested by the subscriber.
23. 23
OperatorstryMap
The map operator allows for any combination of Output and Failure type and passes them through.
tryMap accepts any Input, Failure types, and allows any Output type, but will always output an <Error>
failure type.
30. 30
Operators
Drop
Imagine a scenario where you have a user tapping a button, but you want to ignore all taps until your isReady p
some result. This operator is perfect for this sort of condition.
31. 31
Operators
Prefix
The prefix family of operators is similar to the drop family and provides prefix(_:), prefix(while:) and prefix(untilOu
However, instead of dropping values until some condition is met, the prefix operatorstakevalues until that condition is met.
40. 40
Error handle
Common operators you might want to try out:
● assertNoFailure (which will change the error type to Never and calls an assert
when an error occurs)
● mapError
● retry
● catch
● replaceError
41. 41
Debugging
Fortunately, there are ways to debug in Combine with use of the following operators:
● Print ( to print log messages for all publishing events)
● Breakpoint ( which raises the debugger signal when a provided closure needs to stop the process in the
debugger)
● breakpointOnError (which only raises the
debugger upon receiving a failure)
● Timelane tool
(https://github.com/icanzilb/Timelane/rel
eases/tag/1.3) and add dependency to
project
https://github.com/icanzilb/TimelaneCo
mb
49. 49
Combine in practice
TASK:Imagine that you want to make a single network call that multiple objects subscribe to without making
a new network call.
share()operator returns an instance of the Publishers.Share class. This new publisher “shares” the upstream
publisher. It will subscribe to the upstream publisher once, with the first incoming subscriber.
50. 50
Combine in practice
TASK:Imagine that you want to make a single network call that multiple objects subscribe to without making
a new network call.
51. 51
Combine in practice
TASK:Imagine that you want to make a single network call that multiple objects subscribe to without making a new
network call.
53. 53
Combine in practice
TASK:Imagine that you want to make a single network call that multiple objects subscribe to without making
a new network call.
The reason for this is that the Future executed immediately,
and repeats its output. Reference type.
Wrapping a Future in Deferred makes it behave exactly the same
as any other Publisher
55. 55
Combine in practice
Schedulars
By default, several objects that you might be familiar with already conform to the Scheduler protocol:
1) RunLoop
2) DispatchQueue
3) OperationQueue
When you applyreceive(on:)to a publisher it will
make sure that all events are delivered downstream
on the scheduler you provide. Possition make sence.
Subscribe(on:)- specifies the scheduler on which to
perform subscribe, cancel, and request operations.
59. Background task (exeption URLSession)
59
A lot of publishers will emit values on the queue they received their subscriber on, but this is not always the case.
DataTaskPublisher is set up to publish its values o! the main thread at all ti
60. Multicast
60
A publisher conforming to ConnectablePublisher will have an additional mechanism to start the flow of
data after a subscriber has provided a request. This could be .autoconnect(), which will start the flow
of data as soon as a subscriber requests it. The other option is a separate .connect() call on the publisher itself.
63. 63
List of references :
1. Combine framework
https://medium.com/flawless-app-stories/combine-framework-in-swift-b730ccde131
2. SwiftLeehttps://www.avanderlee.com/swift/combine/
3. Rx vs Combine
https://medium.com/gett-engineering/rxswift-to-apples-combine-cheat-sheet-e9ce
32b14c5b
4. Di!https://dzone.com/articles/combine-vs-rxswift-introduction-to-combine-amp-dif
5. Using Combine - Joseph Heck
6. Combine Asynchronous Programming with Swift_v1.0.2 By Scott Gardner, Shai Mishali,
Florent Pillet & Marin Todorov
7. Yet Another Swift Bloghttps://www.vadimbulavin.com