Slide 1

Slide 1 text

C bine Getting Started with !

Slide 2

Slide 2 text

iOS Tech Lead @ Gett Open Source ❤ Hackathon fan and winner of some International Speaker @freak4pc Ab t Me

Slide 3

Slide 3 text

Involved in dozens of tutorials and several published books Author & Editor at raywenderlich.com iOS Team @freak4pc Ab t Me

Slide 4

Slide 4 text

What is C bine? # $

Slide 5

Slide 5 text

“The Combine framework provides a declarative Swift API for processing values over time. These values can represent many kinds of asynchronous events…” What is Combine?# $

Slide 6

Slide 6 text

“The Combine framework provides a declarative Swift API for processing values over time. These values can represent many kinds of asynchronous events…” What is Combine?# $

Slide 7

Slide 7 text

(almost) Everything is Asynchronous Notification Center
 % KVO
 & Delegates ' Bindings
 ↔ UI
 Events
 ) Networking
 * Publisher !

Slide 8

Slide 8 text

(almost) Everything is Asynchronous Consumers can Subscribe to publishers to be notified on results of that work Publisher Subscriber Hey! I perform work and emit values as they change Cool, I’d like to receive these updated values!

Slide 9

Slide 9 text

No content

Slide 10

Slide 10 text

If all asynchronous work is represented under the same contract, composition becomes extremely powerful. Publisher all the things tap on a button @IBAction user/pass from UITextField(s) .text network request URLSession + Closure tap on a button Publisher user/pass from UITextField(s) Publisher network request Publisher Different interfaces Constant context switching and juggling Same interfaces Composition, unification

Slide 11

Slide 11 text

Subscribing to a piece of information always pushes the latest value from the source, letting you keep your app up to date. Reactive and Declarative let canMakePurchase = productPrice .combineLatest(userBalance) .map { $0 !" $1 }!# Publisher of Bool canMakePurchase ALWAYS reflects the latest state canMakePurchase doesn’t reflect the latest state var userBalance = 5 let productPrice = 10 let canMakePurchase = userBalance !" productPrice print(canMakePurchase) userBalance += 20 print(canMakePurchase) !# false !# STILL false!

Slide 12

Slide 12 text

C bine Some +Theory

Slide 13

Slide 13 text

, Publisher

Slide 14

Slide 14 text

A type that can emit a sequence of values over time , Publisher Type of values this publisher emits Type of error this publisher emits Publisher

Slide 15

Slide 15 text

Publishers can emit 3 types of events Finished Failure (Failure) Completion: Value (Output) Value: , Publisher

Slide 16

Slide 16 text

Publishers may emit values as long as they’re not finished with a completion event Any completion event terminates the publisher and no further values will be published 1 1 2 3 5 8 13 X Publisher 0 1 1 2 3 5 8 13 0 Completed with no error Completed with MyError , Publisher

Slide 17

Slide 17 text

- Subscriber

Slide 18

Slide 18 text

a Publisher is push-based interface, which delivers its values to one or more Subscribers based on their demand - Subscriber Type of values this subscriber receives Type of error this publisher emits Subscriber

Slide 19

Slide 19 text

- Subscriber Subscriber Publisher error type must match output type must match a Publisher is push-based interface, which delivers its values to one or more Subscribers based on their demand

Slide 20

Slide 20 text

The only thing missing is a piece to connect the Subscriber(s) to the Publisher so they can communicate with each other - Subscriber Publisher Subscriber ???

Slide 21

Slide 21 text

. Subscription

Slide 22

Slide 22 text

A subscription represents the connection between a publisher and a subscriber and is where most of the “heavy work” happens . Subscription Publisher Subscriber Subscription

Slide 23

Slide 23 text

The publishers provides a Subscription to the Subscriber via its receive(subscription:) method . Subscription Publisher Subscriber receive(subscription:) Subscription

Slide 24

Slide 24 text

The subscription pushes events to subscribers via 2 different receive methods Subscriber receive(Input) receive(completion: .finished) receive(completion: .failure(Failure)) Values: Completion Event: . Subscription

Slide 25

Slide 25 text

Once you’re done with the specific subscription, you can cancel it using its cancel() method or deallocate the subscription . Subscription cancel() Canceling a subscription will release any memory and resources allocated by the attached subscriber Subscription X

Slide 26

Slide 26 text

/ A subscription also has one final extremely crucial role in Apple’s Combine . Subscription

Slide 27

Slide 27 text

0 Backpressure

Slide 28

Slide 28 text

Backpressure revolves around strategies to deal with Publishers that emit too many values, too rapidly. 0 Backpressure Publisher Subscriber 0 1 2 3 4 5 … 10000 10,000 values over 1 second 12 3

Slide 29

Slide 29 text

The control is transitioned to the Subscriber, which demands a specific amount of values it’s willing to handle 0 Backpressure Publisher Subscriber I’d like to receive up to X values Here are values! I’ll never give you more than you demand Based on these new values, I’d like to adjust my Demand 1 2 3 The publisher must respect the contract and only produce values up to the Subscriber’s demand Types of Demand: Demand.unlimited Demand.none Demand.max(x)

Slide 30

Slide 30 text

4 Subject

Slide 31

Slide 31 text

A Subject is a special kind of publisher you can manually send values to, which is quite useful for bridging imperative code 4 Subject Publisher Subject Immutable (read-only) Values produced internally by the publisher Mutable (read/write)
 Values can be manually pushed by consumer

Slide 32

Slide 32 text

There are two kinds of Subjects - PassthroughSubject and CurrentValueSubject 4 Subject PassthroughSubject Useful for representing events let tappedButton = PassthroughSubject() // Send a “tap” to the Subject, existing subscribers // will immediately receive it tappedButton.send() New subscribers get any values sent after the subscription

Slide 33

Slide 33 text

There are two kinds of Subjects - PassthroughSubject and CurrentValueSubject 4 Subject CurrentValueSubject Useful for representing state // Takes an initial value let isLoading = CurrentValueSubject(false) // Send a value to the subject, like before isLoading.send(true) // Or via the `value` setter isLoading.value = false New subscribers get a replay of the current value

Slide 34

Slide 34 text

⏳ tl;dr

Slide 35

Slide 35 text

⏳ tl;dr Publisher Subscriber App Subscriber attaches to Publisher Publisher.subscribe(_:) Publisher provides Subscription to Subscriber receive(subscription:) Subscriber is provided to Publisher receive(subscriber:) 1 2 3 Subscription First, the Subscriber and Publisher are connected. Once they’re connected, the Publisher provides a Subscription to the Subscriber.

Slide 36

Slide 36 text

Subscriber Subscription At this point, the Subscriber and Subscription interact amongst themselves on how many values the Subscription may produce Hey there, please give me up to X values request(Subscribers.Demand) Here are some values! receive(value:) "# Subscribers.Demand Subscriber may change demand based on the value in an additive way I’m done! Here’s a completion event receive(finished:) 1 2 3 ⏳ tl;dr

Slide 37

Slide 37 text

No content

Slide 38

Slide 38 text

C bine6 In Practice

Slide 39

Slide 39 text

So now I have a bunch of Publishers. What do I actually do with them? , Consuming Publishers #$

Slide 40

Slide 40 text

There are two built-in ways to consume Publishers: sink and assign , Consuming Publishers let publisher = ["hey", "there", “Swift”, “Heroes”].publisher publisher .sink(receiveCompletion: { print("Completed with \($0)" )}, receiveValue: { print("Got value: \($0)") }) sink is a closure-based subscriber: Output: Got value: hey Got value: there Got value: Swift Got value: Heroes Completed with finished

Slide 41

Slide 41 text

, Consuming Publishers assign pushes every emission to the provided key path let publisher = ["hey", "there", "Swift", “Heroes”].publisher publisher .assign(to: \.text, on: textField) Swift there Hey Heroes Publisher assign UITextField.text 1 2 3 4 There are two built-in ways to consume Publishers: sink and assign

Slide 42

Slide 42 text

Getting values is useful, but don’t you wish you could control and modify the values emitted by publishers? , Consuming Publishers ⁉

Slide 43

Slide 43 text

8 Operators

Slide 44

Slide 44 text

8 Operators If Combine would be a language, operators are the words. The better your vocabulary will be, the more things you can do with your data. Operators let you manipulate upstream publishers and control the emissions in extremely expressive and flexible ways Publisher Modified Publisher Operator

Slide 45

Slide 45 text

8 Operators Simple, chainable methods with many same-named variants in the Swift standard library. Publisher publisher .compactMap { Float($0) } .removeDuplicates() .map { ceil($0) } .filter { $0 < 4 } .dropFirst() .append(stride(from: 0.3, to: 0.6, by: 0.1)) Output: .append(stride(from: 0.3, to: 0.6, by: 0.1)) “2.0” “try!” “Swift” “0.2” “1.23” “2”

Slide 46

Slide 46 text

8 Operators Simple, chainable methods with many same-named variants in the Swift standard library. Publisher publisher .compactMap { Float($0) } .removeDuplicates() .map { ceil($0) } .filter { $0 < 4 } .dropFirst() .append(stride(from: 0.3, to: 0.6, by: 0.1)) Output: .append(stride(from: 0.3, to: 0.6, by: 0.1)) “2.0” “try!” “Swift” “0.2” “1.23” “2”

Slide 47

Slide 47 text

8 Operators Simple, chainable methods with many same-named variants in the Swift standard library. Publisher publisher .compactMap { Float($0) } .removeDuplicates() .map { ceil($0) } .filter { $0 < 4 } .dropFirst() .append(stride(from: 0.3, to: 0.6, by: 0.1)) Output: 1.23 .append(stride(from: 0.3, to: 0.6, by: 0.1)) “2.0” “try!” “Swift” “0.2” “2”

Slide 48

Slide 48 text

8 Operators Simple, chainable methods with many same-named variants in the Swift standard library. Publisher publisher .compactMap { Float($0) } .removeDuplicates() .map { ceil($0) } .filter { $0 < 4 } .dropFirst() .append(stride(from: 0.3, to: 0.6, by: 0.1)) Output: 1.23 .append(stride(from: 0.3, to: 0.6, by: 0.1)) “2.0” “try!” “Swift” “0.2” “2”

Slide 49

Slide 49 text

8 Operators Simple, chainable methods with many same-named variants in the Swift standard library. Publisher publisher .compactMap { Float($0) } .removeDuplicates() .map { ceil($0) } .filter { $0 < 4 } .dropFirst() .append(stride(from: 0.3, to: 0.6, by: 0.1)) Output: 1.23 .append(stride(from: 0.3, to: 0.6, by: 0.1)) “2.0” “try!” “Swift” “0.2” “2”

Slide 50

Slide 50 text

8 Operators Simple, chainable methods with many same-named variants in the Swift standard library. Publisher publisher .compactMap { Float($0) } .removeDuplicates() .map { ceil($0) } .filter { $0 < 4 } .dropFirst() .append(stride(from: 0.3, to: 0.6, by: 0.1)) Output: .append(stride(from: 0.3, to: 0.6, by: 0.1)) 2.0 “2.0” “try!” “Swift” “0.2” “2”

Slide 51

Slide 51 text

8 Operators Simple, chainable methods with many same-named variants in the Swift standard library. Publisher publisher .compactMap { Float($0) } .removeDuplicates() .map { ceil($0) } .filter { $0 < 4 } .dropFirst() .append(stride(from: 0.3, to: 0.6, by: 0.1)) Output: .append(stride(from: 0.3, to: 0.6, by: 0.1)) 2.0 “2.0” “try!” “Swift” “0.2” “2”

Slide 52

Slide 52 text

8 Operators Simple, chainable methods with many same-named variants in the Swift standard library. Publisher publisher .compactMap { Float($0) } .removeDuplicates() .map { ceil($0) } .filter { $0 < 4 } .dropFirst() .append(stride(from: 0.3, to: 0.6, by: 0.1)) Output: .append(stride(from: 0.3, to: 0.6, by: 0.1)) 2.0 “2.0” “try!” “Swift” “0.2” “2”

Slide 53

Slide 53 text

8 Operators Simple, chainable methods with many same-named variants in the Swift standard library. Publisher publisher .compactMap { Float($0) } .removeDuplicates() .map { ceil($0) } .filter { $0 < 4 } .dropFirst() .append(stride(from: 0.3, to: 0.6, by: 0.1)) Output: .append(stride(from: 0.3, to: 0.6, by: 0.1)) “2.0” “try!” “Swift” “0.2” “2”

Slide 54

Slide 54 text

8 Operators Simple, chainable methods with many same-named variants in the Swift standard library. Publisher publisher .compactMap { Float($0) } .removeDuplicates() .map { ceil($0) } .filter { $0 < 4 } .dropFirst() .append(stride(from: 0.3, to: 0.6, by: 0.1)) Output: .append(stride(from: 0.3, to: 0.6, by: 0.1)) “2.0” “try!” “Swift” “0.2” “2”

Slide 55

Slide 55 text

8 Operators Simple, chainable methods with many same-named variants in the Swift standard library. Publisher publisher .compactMap { Float($0) } .removeDuplicates() .map { ceil($0) } .filter { $0 < 4 } .dropFirst() .append(stride(from: 0.3, to: 0.6, by: 0.1)) Output: .append(stride(from: 0.3, to: 0.6, by: 0.1)) “2.0” “try!” “Swift” “0.2” “2”

Slide 56

Slide 56 text

8 Operators Simple, chainable methods with many same-named variants in the Swift standard library. Publisher publisher .compactMap { Float($0) } .removeDuplicates() .map { ceil($0) } .filter { $0 < 4 } .dropFirst() .append(stride(from: 0.3, to: 0.6, by: 0.1)) Output: .append(stride(from: 0.3, to: 0.6, by: 0.1)) 2.0 “2.0” “try!” “Swift” “0.2”

Slide 57

Slide 57 text

8 Operators Simple, chainable methods with many same-named variants in the Swift standard library. Publisher publisher .compactMap { Float($0) } .removeDuplicates() .map { ceil($0) } .filter { $0 < 4 } .dropFirst() .append(stride(from: 0.3, to: 0.6, by: 0.1)) Output: .append(stride(from: 0.3, to: 0.6, by: 0.1)) 2.0 “2.0” “try!” “Swift” “0.2”

Slide 58

Slide 58 text

8 Operators Simple, chainable methods with many same-named variants in the Swift standard library. Publisher publisher .compactMap { Float($0) } .removeDuplicates() .map { ceil($0) } .filter { $0 < 4 } .dropFirst() .append(stride(from: 0.3, to: 0.6, by: 0.1)) Output: .append(stride(from: 0.3, to: 0.6, by: 0.1)) 2.0 “2.0” “try!” “Swift” “0.2”

Slide 59

Slide 59 text

8 Operators Simple, chainable methods with many same-named variants in the Swift standard library. Publisher publisher .compactMap { Float($0) } .removeDuplicates() .map { ceil($0) } .filter { $0 < 4 } .dropFirst() .append(stride(from: 0.3, to: 0.6, by: 0.1)) Output: .append(stride(from: 0.3, to: 0.6, by: 0.1)) 2.0 “2.0” “try!” “Swift” “0.2”

Slide 60

Slide 60 text

8 Operators Simple, chainable methods with many same-named variants in the Swift standard library. Publisher publisher .compactMap { Float($0) } .removeDuplicates() .map { ceil($0) } .filter { $0 < 4 } .dropFirst() .append(stride(from: 0.3, to: 0.6, by: 0.1)) Output: 2.0 .append(stride(from: 0.3, to: 0.6, by: 0.1)) “2.0” “try!” “Swift” “0.2”

Slide 61

Slide 61 text

8 Operators Simple, chainable methods with many same-named variants in the Swift standard library. Publisher publisher .compactMap { Float($0) } .removeDuplicates() .map { ceil($0) } .filter { $0 < 4 } .dropFirst() .append(stride(from: 0.3, to: 0.6, by: 0.1)) Output: 2.0 .append(stride(from: 0.3, to: 0.6, by: 0.1)) “2.0” “try!” “Swift” “0.2”

Slide 62

Slide 62 text

8 Operators Simple, chainable methods with many same-named variants in the Swift standard library. Publisher publisher .compactMap { Float($0) } .removeDuplicates() .map { ceil($0) } .filter { $0 < 4 } .dropFirst() .append(stride(from: 0.3, to: 0.6, by: 0.1)) Output: 2.0 .append(stride(from: 0.3, to: 0.6, by: 0.1)) “try!” “Swift” “0.2” 2.0

Slide 63

Slide 63 text

8 Operators Simple, chainable methods with many same-named variants in the Swift standard library. Publisher publisher .compactMap { Float($0) } .removeDuplicates() .map { ceil($0) } .filter { $0 < 4 } .dropFirst() .append(stride(from: 0.3, to: 0.6, by: 0.1)) Output: 2.0 .append(stride(from: 0.3, to: 0.6, by: 0.1)) “try!” “Swift” “0.2” 2.0

Slide 64

Slide 64 text

8 Operators Simple, chainable methods with many same-named variants in the Swift standard library. Publisher publisher .compactMap { Float($0) } .removeDuplicates() .map { ceil($0) } .filter { $0 < 4 } .dropFirst() .append(stride(from: 0.3, to: 0.6, by: 0.1)) Output: 2.0 .append(stride(from: 0.3, to: 0.6, by: 0.1)) “try!” “Swift” “0.2” 2.0

Slide 65

Slide 65 text

8 Operators Simple, chainable methods with many same-named variants in the Swift standard library. Publisher publisher .compactMap { Float($0) } .removeDuplicates() .map { ceil($0) } .filter { $0 < 4 } .dropFirst() .append(stride(from: 0.3, to: 0.6, by: 0.1)) Output: 2.0 .append(stride(from: 0.3, to: 0.6, by: 0.1)) “try!” “Swift” “0.2”

Slide 66

Slide 66 text

8 Operators Simple, chainable methods with many same-named variants in the Swift standard library. Publisher publisher .compactMap { Float($0) } .removeDuplicates() .map { ceil($0) } .filter { $0 < 4 } .dropFirst() .append(stride(from: 0.3, to: 0.6, by: 0.1)) Output: 2.0 .append(stride(from: 0.3, to: 0.6, by: 0.1)) “try!” “Swift” “0.2”

Slide 67

Slide 67 text

8 Operators Simple, chainable methods with many same-named variants in the Swift standard library. Publisher publisher .compactMap { Float($0) } .removeDuplicates() .map { ceil($0) } .filter { $0 < 4 } .dropFirst() .append(stride(from: 0.3, to: 0.6, by: 0.1)) Output: 2.0 .append(stride(from: 0.3, to: 0.6, by: 0.1)) “0.2” nil nil

Slide 68

Slide 68 text

8 Operators Simple, chainable methods with many same-named variants in the Swift standard library. Publisher publisher .compactMap { Float($0) } .removeDuplicates() .map { ceil($0) } .filter { $0 < 4 } .dropFirst() .append(stride(from: 0.3, to: 0.6, by: 0.1)) Output: 2.0 .append(stride(from: 0.3, to: 0.6, by: 0.1)) “0.2”

Slide 69

Slide 69 text

8 Operators Simple, chainable methods with many same-named variants in the Swift standard library. Publisher publisher .compactMap { Float($0) } .removeDuplicates() .map { ceil($0) } .filter { $0 < 4 } .dropFirst() .append(stride(from: 0.3, to: 0.6, by: 0.1)) Output: 2.0 .append(stride(from: 0.3, to: 0.6, by: 0.1)) “0.2”

Slide 70

Slide 70 text

8 Operators Simple, chainable methods with many same-named variants in the Swift standard library. Publisher publisher .compactMap { Float($0) } .removeDuplicates() .map { ceil($0) } .filter { $0 < 4 } .dropFirst() .append(stride(from: 0.3, to: 0.6, by: 0.1)) Output: 2.0 .append(stride(from: 0.3, to: 0.6, by: 0.1)) 0.2

Slide 71

Slide 71 text

8 Operators Simple, chainable methods with many same-named variants in the Swift standard library. Publisher publisher .compactMap { Float($0) } .removeDuplicates() .map { ceil($0) } .filter { $0 < 4 } .dropFirst() .append(stride(from: 0.3, to: 0.6, by: 0.1)) Output: 2.0 .append(stride(from: 0.3, to: 0.6, by: 0.1)) 0.2

Slide 72

Slide 72 text

8 Operators Simple, chainable methods with many same-named variants in the Swift standard library. Publisher publisher .compactMap { Float($0) } .removeDuplicates() .map { ceil($0) } .filter { $0 < 4 } .dropFirst() .append(stride(from: 0.3, to: 0.6, by: 0.1)) Output: 2.0 .append(stride(from: 0.3, to: 0.6, by: 0.1)) 0.2

Slide 73

Slide 73 text

8 Operators Simple, chainable methods with many same-named variants in the Swift standard library. Publisher publisher .compactMap { Float($0) } .removeDuplicates() .map { ceil($0) } .filter { $0 < 4 } .dropFirst() .append(stride(from: 0.3, to: 0.6, by: 0.1)) Output: 2.0 .append(stride(from: 0.3, to: 0.6, by: 0.1)) 1.0

Slide 74

Slide 74 text

8 Operators Simple, chainable methods with many same-named variants in the Swift standard library. Publisher publisher .compactMap { Float($0) } .removeDuplicates() .map { ceil($0) } .filter { $0 < 4 } .dropFirst() .append(stride(from: 0.3, to: 0.6, by: 0.1)) Output: 2.0 .append(stride(from: 0.3, to: 0.6, by: 0.1)) 1.0

Slide 75

Slide 75 text

8 Operators Simple, chainable methods with many same-named variants in the Swift standard library. Publisher publisher .compactMap { Float($0) } .removeDuplicates() .map { ceil($0) } .filter { $0 < 4 } .dropFirst() .append(stride(from: 0.3, to: 0.6, by: 0.1)) Output: 2.0 .append(stride(from: 0.3, to: 0.6, by: 0.1)) 1.0

Slide 76

Slide 76 text

8 Operators Simple, chainable methods with many same-named variants in the Swift standard library. Publisher publisher .compactMap { Float($0) } .removeDuplicates() .map { ceil($0) } .filter { $0 < 4 } .dropFirst() .append(stride(from: 0.3, to: 0.6, by: 0.1)) Output: 2.0 .append(stride(from: 0.3, to: 0.6, by: 0.1)) 1.0

Slide 77

Slide 77 text

8 Operators Simple, chainable methods with many same-named variants in the Swift standard library. Publisher publisher .compactMap { Float($0) } .removeDuplicates() .map { ceil($0) } .filter { $0 < 4 } .dropFirst() .append(stride(from: 0.3, to: 0.6, by: 0.1)) Output: 2.0 1.0

Slide 78

Slide 78 text

8 Operators Simple, chainable methods with many same-named variants in the Swift standard library. Publisher publisher .compactMap { Float($0) } .removeDuplicates() .map { ceil($0) } .filter { $0 < 4 } .dropFirst() .append(stride(from: 0.3, to: 0.6, by: 0.1)) Output: 2.0 1.0 0.3 0.4 0.5

Slide 79

Slide 79 text

8 Operators Simple, chainable methods with many same-named variants in the Swift standard library. Publisher publisher .compactMap { Float($0) } .removeDuplicates() .map { ceil($0) } .filter { $0 < 4 } .dropFirst() .append(stride(from: 0.3, to: 0.6, by: 0.1)) Output: 2.0 1.0 0.3 0.4 0.5 Finished

Slide 80

Slide 80 text

8 Operators Operators are publishers themselves Each operator’s type wraps the previous one in the chain publisher .compactMap { Float($0) } .removeDuplicates() .map { ceil($0) } .filter { $0 < 4 } .dropFirst() .append(stride(from: 0.3, to: 0.6, by: 0.1)) // Publishers.CompactMap, Float> // Publishers.RemoveDuplicates

Slide 81

Slide 81 text

8 Operators Operators are publishers themselves Each operator’s type wraps the previous one in the chain publisher .compactMap { Float($0) } .removeDuplicates() .map { ceil($0) } .filter { $0 < 4 } .dropFirst() .append(stride(from: 0.3, to: 0.6, by: 0.1)) // Publishers.CompactMap, Float> // Publishers.RemoveDuplicates, %oat &, %oat '

Slide 82

Slide 82 text

8 Operators Operators are publishers themselves Each operator’s type wraps the previous one in the chain publisher .compactMap { Float($0) } .removeDuplicates() .map { ceil($0) } .filter { $0 < 4 } .dropFirst() .append(stride(from: 0.3, to: 0.6, by: 0.1)) // Publishers.CompactMap, Float> // Publishers.RemoveDuplicates, %oat &, %oat ' 3

Slide 83

Slide 83 text

8 Operators Instead, you can flatten the Publisher using Type Erasure, and hide the implementation details of your chain publisher .compactMap { Float($0) } .removeDuplicates() .map { ceil($0) } .filter { $0 < 4 } .dropFirst() .append(stride(from: 0.3, to: 0.6, by: 0.1)) // Publishers.CompactMap, Float> // Publishers.RemoveDuplicates

Slide 84

Slide 84 text

8 Operators One more operator you should know is print() As the name suggests, it prints any events passing through it Console Output: publisher // Emits “MobOS”, “Conf” .print("publisher") .map { $0.uppercased() } .print(“uppercased") publisher: receive subscription uppercased: receive subscription: (Print) uppercased: request unlimited publisher: request unlimited publisher: receive value: (MobOS) uppercased: receive value: (MOBOS) publisher: receive value: (Conf) uppercased: receive value: (CONF) publisher: receive finished uppercased: receive finished

Slide 85

Slide 85 text

8 Operators Since publishers unifies how work is represented, operators can be used on any kind of work you need to do map retry map retry Publisher Network Request Publisher Video Decoding

Slide 86

Slide 86 text

C bine 9 In Foundation

Slide 87

Slide 87 text

9 Combine in Foundation Foundation provides some extremely useful publisher abstractions to wrap common constructs ["a", "b", "c"].publisher // "a", "b", "c" (1...5).publisher // 1, 2, 3, 4, 5 stride(from: 0, to: 100, by: 25).publisher // 0, 25, 50, 75 [“conf": "AppBuilders", "year": 2020].publisher // ("conf", "AppBuilders"), ("year": 2020) Sequence.publisher

Slide 88

Slide 88 text

Foundation provides some extremely useful publisher abstractions to wrap common constructs NotificationCenter.publisher 9 Combine in Foundation

Slide 89

Slide 89 text

9 Combine in Foundation Baby Ethan

Slide 90

Slide 90 text

9 Combine in Foundation Baby Ethan

Slide 91

Slide 91 text

9 Combine in Foundation Hey daddy, can you tell me a joke? Sure son, no problem But with Combine, or i’ll cry!

Slide 92

Slide 92 text

Foundation provides some extremely useful publisher abstractions to wrap common constructs URLSession.shared .dataTaskPublisher(for: request) // Publisher<(Data, URLResponse), URLError> URLSession.dataTaskPublisher 9 Combine in Foundation var request = URLRequest(url: URL(string: "https://icanhazdadjoke.com/")!) request.allHTTPHeaderFields = ["Accept": "text/plain"] .sink(receiveCompletion: { finished in switch finished { case .failure(let error): print("An error occurred! \(error)") case .finished: print("Completed with no issues") } }, receiveValue: { data, response in print(String(data: data, encoding: .utf8) ?? Data()) // Can a kangaroo jump higher than the Empire State Building? // Of course. The Empire State Building can't jump. :; })

Slide 93

Slide 93 text

Foundation provides some extremely useful publisher abstractions to wrap common constructs URLSession.shared .dataTaskPublisher(for: request) // Publisher<(Data, URLResponse), URLError> .map(\.data) // Publisher .decode(type: Joke.self, decoder: JSONDecoder()) .sink(receiveCompletion: { finished in // Handle completion event }, receiveValue: { joke in print(joke) // Joke(id: 1337, // joke: “I used to work in a shoe recycling shop. // It was sole destroying. <”) }) URLSession.dataTaskPublisher 9 Combine in Foundation var request = URLRequest(url: URL(string: "https://icanhazdadjoke.com/")!) request.allHTTPHeaderFields = ["Accept": “application/json"] .decode(type: Joke.self, decoder: JSONDecoder())

Slide 94

Slide 94 text

Foundation provides some extremely useful publisher abstractions to wrap common constructs scrollView.publisher(for: \.contentOffset) // Publisher avPlayer.publisher(for: \.status) // Publisher operation.publisher(for: \.queuePriority) // Publisher Any KVO-compliant Key Paths 9 Combine in Foundation

Slide 95

Slide 95 text

And one more treat masked as a property wrapper, @Published makes any variable a publisher @Published 9 Combine in Foundation $isLoading .map(!) .sink(receiveValue: { print("The opposite of loading is \($0)") }) @Published var isLoading: Bool = false isLoading // current value, accessed imperatively $isLoading // Publisher isLoading = true // Mutating notifies any subscribers

Slide 96

Slide 96 text

C bine & SwiftUI !

Slide 97

Slide 97 text

Combine & SwiftUI Combine’s capabilities are deeply rooted within SwiftUI, usually masked under property wrappers ObservableObject !

Slide 98

Slide 98 text

Combine & SwiftUI Combine’s capabilities are deeply rooted within SwiftUI, usually masked under property wrappers @ObservedObject !

Slide 99

Slide 99 text

Combine & SwiftUI Combine’s capabilities are deeply rooted within SwiftUI, usually masked under property wrappers @EnvironmentObject !

Slide 100

Slide 100 text

Combine & SwiftUI Combine’s capabilities are deeply rooted within SwiftUI, usually masked under property wrappers View.onReceive(_:perform:) !

Slide 101

Slide 101 text

Combine & SwiftUI ! And much m e …

Slide 102

Slide 102 text

C munity = 6 >

Slide 103

Slide 103 text

> Community = Combine conceptually builds upon many existing projects and the Reactive Streams spec, along with ones in the Swift ecosystem, for example RxSwift and ReactiveSwift. Luckily, much of your knowledge in either of them is applicable to Combine !

Slide 104

Slide 104 text

iOS Deployment Target iOS 8+ iOS 13+ Supported Platforms iOS, macOS, tvOS, watchOS, Linux iOS, macOS, tvOS, watchOS Maintained By Open Source Apple UI Bindings RxCocoa, ReactiveCocoa SwiftUI (kinda) assign (Not specific to UIKit), no publishers Error model ReactiveSwift: Typed Errors RxSwift: No Typed Errors Typed Errors Backpressure No Yes > Community = ! Combine vs. existing projects

Slide 105

Slide 105 text

Open-source Combine projects > Community = CombineCommunity/CombineCocoa UIKit bindings and extensions for Combine CombineCommunity/CombineDataSources Table and Collection data sources for Combine CombineCommunity/CombineExt Useful extensions Combine is missing ? sergdort/CombineFeedback Feedback-loop styled architecture based on Combine A pointfreeco/swift-composable-architecture Combine-based advanced architecture @ CombineCommunity/RxCombine Bi-directional type bridges between Combine and RxSwift

Slide 106

Slide 106 text

Open-source Combine projects > Community = CombineCommunity/CombineCocoa UIKit bindings and extensions for Combine CombineCommunity/CombineDataSources Table and Collection data sources for Combine CombineCommunity/CombineExt Useful extensions Combine is missing ? sergdort/CombineFeedback Feedback-loop styled architecture based on Combine A pointfreeco/swift-composable-architecture Combine-based advanced architecture @ CombineCommunity/RxCombine Bi-directional type bridges between Combine and RxSwift https://combine.community

Slide 107

Slide 107 text

Use C bine? Should I /

Slide 108

Slide 108 text

Should I use Combine? / Is this a brand new iOS 13 and up project? Yes, Combine makes sense for your scenario! (especially if using SwiftUI) Is this an existing codebase ? Have some existing RxSwift code? Use something like RxCombine to migrate No existing Reactive code As long as you support iOS 13 and above Like anything, it depends…

Slide 109

Slide 109 text

Le n m e? Want to B+

Slide 110

Slide 110 text

B Want to learn more? + https://bit.ly/getcombinebook

Slide 111

Slide 111 text

Shai Mishali freak4pc Thank y !