Upgrade to Pro — share decks privately, control downloads, hide ads and more …

打開 Combine 的引擎蓋看看

F52f4adb291c5b44bd8ad1433dfedaaf?s=47 Tsungyu Yu
November 08, 2020

打開 Combine 的引擎蓋看看

2020 年 iPlayground 演講:
Combine 是怎麼在 Swift 原本的物件導向設計與指令式設計的架構下,編寫出使用方法完全不一樣的 Combine。這個 Session 將討論 Combine 關鍵角色: Publishers、Subscribers、Operators,了解 Combine 如何運作!

F52f4adb291c5b44bd8ad1433dfedaaf?s=128

Tsungyu Yu

November 08, 2020
Tweet

Transcript

  1. 打開 Combine 的引擎蓋看看 SWIFT, COMBINE, REACTIVE IPLAYGROUND 2020 游諭 What’s

    under the hood?
  2. 游諭(@ytyubox) 
 ⾃學程式 iOS - 2018 iT 邦幫忙鐵⼈賽: 
 30

    天了解 Swift 的 Combine 
 30 天從 Swift 學會 Objective-C $> whoami 2
  3. Agenda Short Introductions 簡單的 Combine 應⽤ combine 的⾓⾊ Behavior test

    更進階的 Combine 3 ⽬錄
  4. Agenda Short Introductions 簡單的 Combine 應⽤ combine 的⾓⾊ Behavior test

    更進階的 Combine 4 ⽬錄 50 % +
  5. 5 開始之前

  6. 6 ,這個 Session 將會 介紹如何入⾨ Combine 並不包含解釋 Framework 內部原始碼 開始之前

  7. Short Introductions 7

  8. 8

  9. 9 SwiftUI

  10. 10 Combine SwiftUI

  11. RxSwift 11 https://github.com/ReactiveX https://github.com/ReactiveCocoa/ReactiveSwift https://developer.apple.com/videos/wwdc2019 Combine

  12. 12 https://github.com/ReactiveX https://github.com/ReactiveCocoa/ReactiveSwift https://developer.apple.com/videos/wwdc2019 Functional Reactive Programming Combine RxSwift

  13. 13 A unified, declarative API for 
 processing values over

    time. By WWDC 2019: Introducing Combine Functional Reactive Programming Combine
  14. 14 A unified, declarative API for 
 processing values over

    time. By WWDC 2019: Introducing Combine Functional Reactive Programming Combine
  15. 15 A unified, declarative API for 
 processing values over

    time. Generic Composition f i rst Request driven type safe By WWDC 2019: Introducing Combine Combine
  16. 打開 Combine 會發現什麼? 16

  17. 17 打開 Combine 發現了⼀隻貓

  18. Asynchronous Programming 18

  19. 來⾃ Apple / Combine 19 透過組合處理事件的運算⼦來定制異步事件的應對⽅式。

  20. 來⾃ Apple / Combine 20 透過組合處理事件的運算⼦來定制異步事件的應對⽅式。

  21. 來⾃ Apple / Combine 21 透過組合處理事件的運算⼦來定制異步事件的應對⽅式。 注意 Apple 這裡的非同步不是指 Dispatch

    framework
  22. 22 asynchronous programing 非同步程式設計

  23. Property Observers ( set / didSet ) Target / Action

    ( @IBAction ) Notification Center Ad-hoc Callbacks 23 asynchronous programing 非同步程式設計 URLSession.dataTask(with:, completionHandler:) Key-Value Observing
  24. 24 Key-Value Observing 複習⼀下

  25. 25 class KVOTarget: NSObject { @objc dynamic var value =

    0 } let target = KVOTarget() var captured = [Int?]() let kvoToken = target.observe(\.value, options: .new) { (target, change) in captured.append(change.newValue) } XCTAssertEqual(captured, []) target.value = 1 target.value = 2 XCTAssertEqual(captured, [1, 2]) kvoToken.invalidate() target.value = 3 XCTAssertEqual(captured, [1, 2]) Key-Value Observing 複習⼀下
  26. 26 class KVOTarget: NSObject { @objc dynamic var value =

    0 } let target = KVOTarget() var captured = [Int?]() let kvoToken = target.observe(\.value, options: .new) { (target, change) in captured.append(change.newValue) } XCTAssertEqual(captured, []) target.value = 1 target.value = 2 XCTAssertEqual(captured, [1, 2]) kvoToken.invalidate() target.value = 3 XCTAssertEqual(captured, [1, 2]) Key-Value Observing 複習⼀下
  27. 27 class KVOTarget: NSObject { @objc dynamic var value =

    0 } let target = KVOTarget() var captured = [Int?]() let kvoToken = target.observe(\.value, options: .new) { (target, change) in captured.append(change.newValue) } XCTAssertEqual(captured, []) target.value = 1 target.value = 2 XCTAssertEqual(captured, [1, 2]) kvoToken.invalidate() target.value = 3 XCTAssertEqual(captured, [1, 2]) Key-Value Observing 複習⼀下
  28. 28 class KVOTarget: NSObject { @objc dynamic var value =

    0 } let target = KVOTarget() var captured = [Int?]() let kvoToken = target.observe(\.value, options: .new) { (target, change) in captured.append(change.newValue) } XCTAssertEqual(captured, []) target.value = 1 target.value = 2 XCTAssertEqual(captured, [1, 2]) kvoToken.invalidate() target.value = 3 XCTAssertEqual(captured, [1, 2]) Key-Value Observing 複習⼀下
  29. 簡單 Combine 應⽤ 29

  30. 30 import Combine

  31. 31 URLSession.shared.dataTaskPublisher(for: aURL) import Combine

  32. 32 URLSession.shared.dataTaskPublisher(for: aURL) Publisher import Combine

  33. 33 URLSession.shared.dataTaskPublisher(for: aURL) .map(\.data) .decode(type: MyModel.self, decoder: JSONDecoder()) Publisher import

    Combine
  34. 34 URLSession.shared.dataTaskPublisher(for: aURL) .map(\.data) .decode(type: MyModel.self, decoder: JSONDecoder()) .sink(receiveCompletion: self.receiveCompletionMethod,

    
 receiveValue: self.receiveValueMethod) AnyCancellable import Combine
  35. 35 import Combine func greet(person: String) throws -> String {

    let greeting = "Hello, " + person + "!" return greeting }
  36. 36 func greetPublisher(person: String) -> AnyPublisher<String,Error> { return Just(person) .prepend("Hello,

    ") .append("!") .reduce("", +) .eraseToAnyPublisher() } func greet(person: String) throws -> String { let greeting = "Hello, " + person + "!" return greeting } import Combine
  37. Combine ⾓⾊ 37

  38. 38 import Combine protocol Publisher { associatedtype Output associatedtype Failure

    : Error func receive<S: Subscriber>(subscriber: S) where Self.Failure == S.Failure, Self.Output == S.Input }
  39. 39 import Combine protocol Publisher { ... }

  40. 40 import Combine protocol Publisher { ... } protocol Subscriber

    { associatedtype Input associatedtype Failure : Error func receive(subscription: Subscription) func receive(_ input: Self.Input) -> Subscribers.Demand func receive(completion: Subscribers.Completion<Self.Failure>) }
  41. 41 import Combine protocol Publisher { ... } protocol Subscriber

    { ... }
  42. protocol Publisher { ... } protocol Subscriber { ... }

    42 Subscriber Publisher import Combine
  43. Subscriber Publisher 43

  44. Subscriber Publisher 44

  45. Subscriber Publisher 45

  46. Subscriber Publisher 46

  47. Subscriber Publisher 47

  48. Subscriber Publisher 48

  49. Subscriber 49

  50. 50 Combine Pattern From WWDC 2019 - Introducing Combine

  51. 51 Combine Pattern From WWDC 2019 - Introducing Combine 來⾃與

    Introducing Combine https://developer.apple.com/videos/play/wwdc2019/722/
  52. Subscriber Publisher subscribe( ) is attached Publisher Subscriber Combine Pattern

    From WWDC 2019 - Introducing Combine 52
  53. Subscriber Publisher subscribe( ) is attached Publisher Subscriber Subscription receive(subscription:

    ) sends Publisher Subscription Combine Pattern From WWDC 2019 - Introducing Combine 53
  54. Subscriber Publisher subscribe( ) is attached Publisher Subscriber request(_: Demand)

    requests N values Subscriber Subscription receive(subscription: ) sends Publisher Subscription Combine Pattern From WWDC 2019 - Introducing Combine 54
  55. Subscriber Publisher subscribe( ) is attached Publisher Subscriber receive(_: Input)

    sends N values or less Publisher request(_: Demand) requests N values Subscriber Subscription receive(subscription: ) sends Publisher Subscription Combine Pattern From WWDC 2019 - Introducing Combine 55
  56. Subscriber Publisher subscribe( ) is attached Publisher Subscriber receive(_: Input)

    sends N values or less Publisher receive(completion:) sends completion Publisher request(_: Demand) requests N values Subscriber Subscription receive(subscription: ) sends Publisher Subscription Combine Pattern From WWDC 2019 - Introducing Combine 56
  57. 57 如果對⼀個 Publisher 重複 Subscribe 呢?

  58. Publisher Subscriber 58 Subscriber No. 2

  59. Publisher Subscriber 59 Subscriber No. 2

  60. Publisher Subscriber 60 Subscriber No. 2

  61. Publisher Subscriber 61 Subscriber No. 2

  62. Publisher Subscriber 62 Subscriber No. 2 Copied Publisher

  63. Publisher Subscriber 63 Subscriber No. 2 Copied Publisher

  64. Publisher Subscriber 64 Subscriber No. 2 Copied Publisher

  65. Publisher Subscriber 65 Subscriber No. 2 Copied Publisher

  66. Subscriber Publisher subscribe( ) receive(_: Input) sends N values or

    less Publisher receive(completion:) sends completion Publisher request(_: Demand) Subscription receive(subscription: ) is attached Publisher Subscriber requests N values Subscriber sends Publisher Subscription Combine Pattern From WWDC 2019 - Introducing Combine 66
  67. receive(subscription: ) Publisher 67 Subscriber subscribe( ) request(_: Demand) receive(_:

    Input) receive(completion:) Subscription subscribe( ) receive(_: Input) sends N values or less receive(completion:) sends completion request(_: Demand) Subscription Combine Pattern From my perspective Subscription Subscription is attached Publisher Subscriber requests N values Subscriber sends Publisher Subscription
  68. Subscription Publisher Subscriber 68

  69. Combine Operator 69

  70. Publisher <Int> Publisher <String> Mapper <Int, String> 70 Operator

  71. 71 Operator 其實是 decorator pattern

  72. Publisher Operator class Operator: Publisher { let concrete: Publisher }

    is a has a 72 decorator pattern
  73. client decor 1 driver decor 2 action 73 decorator pattern

  74. client decor 1 driver decor 2 action addBehavior 74 decorator

    pattern
  75. client decor 1 driver decor 2 action action 75 decorator

    pattern addBehavior
  76. client decor 1 driver decor 2 action action action 76

    decorator pattern addBehavior
  77. addBehavior client decor 1 driver decor 2 action action action

    77 decorator pattern addBehavior
  78. client decor 1 driver decor 2 action action action 78

    decorator pattern addBehavior addBehavior addBehavior
  79. Mazinger Z: a Japanese super robot manga series written and

    illustrated by Go Naga Has a Man, is a Man. 79 無敵鐵⾦剛
  80. 80 Behavior tests

  81. 81 AnyCancellable deinit 會⾃動 Cancel

  82. 82 let subject = PassthroughSubject<Int, Never>() var received = [Event<Int,

    Never>]() weak var weakCancellable: AnyCancellable? do { let anyCancellable = subject .sink(event: { e in received.append(e) } ) weakCancellable = anyCancellable subject.send(1) } XCTAssertNil(weakCancellable) subject.send(2) XCTAssertEqual(received, [1]) AnyCancellable deinit 會⾃動 Cancel
  83. 83 let subject = PassthroughSubject<Int, Never>() var received = [Event<Int,

    Never>]() weak var weakCancellable: AnyCancellable? do { let anyCancellable = subject .sink(event: { e in received.append(e) } ) weakCancellable = anyCancellable subject.send(1) } XCTAssertNil(weakCancellable) subject.send(2) XCTAssertEqual(received, [1]) AnyCancellable deinit 會⾃動 Cancel
  84. 84 let subject = PassthroughSubject<Int, Never>() var received = [Event<Int,

    Never>]() weak var weakCancellable: AnyCancellable? do { let anyCancellable = subject .sink(event: { e in received.append(e) } ) weakCancellable = anyCancellable subject.send(1) } XCTAssertNil(weakCancellable) subject.send(2) XCTAssertEqual(received, [1]) AnyCancellable deinit 會⾃動 Cancel
  85. 85 let subject = PassthroughSubject<Int, Never>() var received = [Event<Int,

    Never>]() weak var weakCancellable: AnyCancellable? do { let anyCancellable = subject .sink(event: { e in received.append(e) } ) weakCancellable = anyCancellable subject.send(1) } XCTAssertNil(weakCancellable) subject.send(2) XCTAssertEqual(received, [1]) AnyCancellable deinit 會⾃動 Cancel
  86. 86 let subject = PassthroughSubject<Int, Never>() var received = [Event<Int,

    Never>]() weak var weakCancellable: AnyCancellable? do { let anyCancellable = subject .sink(event: { e in received.append(e) } ) weakCancellable = anyCancellable subject.send(1) } XCTAssertNil(weakCancellable) subject.send(2) XCTAssertEqual(received, [1]) AnyCancellable deinit 會⾃動 Cancel
  87. 87 let subject = PassthroughSubject<Int, Never>() var received = [Event<Int,

    Never>]() weak var weakCancellable: AnyCancellable? do { let anyCancellable = subject .sink(event: { e in received.append(e) } ) weakCancellable = anyCancellable subject.send(1) } XCTAssertNil(weakCancellable) subject.send(2) XCTAssertEqual(received, [1]) AnyCancellable deinit 會⾃動 Cancel
  88. 88 NSKeyValueObservation deinit 也會⾃動 Cancel

  89. 89 let target = KVOTarget() var captured = [Int?]() weak

    var weakKvoToken: NSKeyValueObservation? do { let token = target.observe(\.value, options: .new) { (target, change) in captured.append(change.newValue) } weakKvoToken = token target.value = 1 target.value = 2 XCTAssertEqual(captured, [1, 2]) } XCTAssertNil(weakKvoToken) target.value = 3 XCTAssertEqual(captured, [1, 2]) NSKeyValueObservation deinit 也會⾃動 Cancel
  90. let target = KVOTarget() var captured = [Int?]() weak var

    weakKvoToken: NSKeyValueObservation? do { let token = target.observe(\.value, options: .new) { (target, change) in captured.append(change.newValue) } weakKvoToken = token target.value = 1 target.value = 2 XCTAssertEqual(captured, [1, 2]) } XCTAssertNil(weakKvoToken) target.value = 3 XCTAssertEqual(captured, [1, 2]) 90 NSKeyValueObservation deinit 也會⾃動 Cancel
  91. let target = KVOTarget() var captured = [Int?]() weak var

    weakKvoToken: NSKeyValueObservation? do { let token = target.observe(\.value, options: .new) { (target, change) in captured.append(change.newValue) } weakKvoToken = token target.value = 1 target.value = 2 XCTAssertEqual(captured, [1, 2]) } XCTAssertNil(weakKvoToken) target.value = 3 XCTAssertEqual(captured, [1, 2]) 91 NSKeyValueObservation deinit 也會⾃動 Cancel
  92. 92 使⽤ Publisher.subscribe 會 Retain Sink

  93. 93 let subject = PassthroughSubject<Int, Never>() var received = [Event<Int,

    Never>]() weak var weakSink: Subscribers.Sink<Int, Never>? do { let sink = Subscribers.Sink<Int, Never>{ received.append(.value($0)) } weakSink = sink subject.subscribe(sink) subject.send(1) } XCTAssertNotNil(weakSink) subject.send(2) weakSink?.cancel() XCTAssertNil(weakSink) subject.send(3) XCTAssertEqual(received, [1, 2]) 使⽤ Publisher.subscribe 會 Retain Sink
  94. 94 let subject = PassthroughSubject<Int, Never>() var received = [Event<Int,

    Never>]() weak var weakSink: Subscribers.Sink<Int, Never>? do { let sink = Subscribers.Sink<Int, Never>{ received.append(.value($0)) } weakSink = sink subject.subscribe(sink) subject.send(1) } XCTAssertNotNil(weakSink) subject.send(2) weakSink?.cancel() XCTAssertNil(weakSink) subject.send(3) XCTAssertEqual(received, [1, 2]) 使⽤ Publisher.subscribe 會 Retain Sink
  95. 95 let subject = PassthroughSubject<Int, Never>() var received = [Event<Int,

    Never>]() weak var weakSink: Subscribers.Sink<Int, Never>? do { let sink = Subscribers.Sink<Int, Never>{ received.append(.value($0)) } weakSink = sink subject.subscribe(sink) subject.send(1) } XCTAssertNotNil(weakSink) subject.send(2) weakSink?.cancel() XCTAssertNil(weakSink) subject.send(3) XCTAssertEqual(received, [1, 2]) 使⽤ Publisher.subscribe 會 Retain Sink
  96. 96 let subject = PassthroughSubject<Int, Never>() var received = [Event<Int,

    Never>]() weak var weakSink: Subscribers.Sink<Int, Never>? do { let sink = Subscribers.Sink<Int, Never>{ received.append(.value($0)) } weakSink = sink subject.subscribe(sink) subject.send(1) } XCTAssertNotNil(weakSink) subject.send(2) weakSink?.cancel() XCTAssertNil(weakSink) subject.send(3) XCTAssertEqual(received, [1, 2]) 使⽤ Publisher.subscribe 會 Retain Sink
  97. 97 let subject = PassthroughSubject<Int, Never>() var received = [Event<Int,

    Never>]() weak var weakSink: Subscribers.Sink<Int, Never>? do { let sink = Subscribers.Sink<Int, Never>{ received.append(.value($0)) } weakSink = sink subject.subscribe(sink) subject.send(1) } XCTAssertNotNil(weakSink) subject.send(2) weakSink?.cancel() XCTAssertNil(weakSink) subject.send(3) XCTAssertEqual(received, [1, 2]) 使⽤ Publisher.subscribe 會 Retain Sink
  98. 98 let subject = PassthroughSubject<Int, Never>() var received = [Event<Int,

    Never>]() weak var weakSink: Subscribers.Sink<Int, Never>? do { let sink = Subscribers.Sink<Int, Never>{ received.append(.value($0)) } weakSink = sink subject.subscribe(sink) subject.send(1) } XCTAssertNotNil(weakSink) subject.send(2) weakSink?.cancel() XCTAssertNil(weakSink) subject.send(3) XCTAssertEqual(received, [1, 2]) 使⽤ Publisher.subscribe 會 Retain Sink
  99. 更進階的 Combine 99

  100. 在開始之前:習慣 Value over time 100

  101. 複習⼀下 ”國中物理“ 直線等加速度運動圖形 101

  102. 複習⼀下 ”國中物理“ 直線等加速度運動圖形 102 時間 速 度 時間 位 移

    時間 加 速 度
  103. 複習⼀下 ”國中物理“ 直線等加速度運動圖形 103 時間 速 度 時間 位 移

    時間 加 速 度
  104. 104 彈珠圖 marble diagram 事件 Event 截⽌ Completion ❌ Error

    事件流 Stream
  105. 105 彈珠圖 marble diagram 事件流 Stream ❌ Error

  106. 106 Publisher Subscribe 多次

  107. 107 Subscriber Subscriber Publisher Publisher Subscribe 多次

  108. 108 Subscriber Subscriber Publisher Publisher Subscribe 多次

  109. 109 Subscriber Subscriber Publisher Publisher Subscribe 多次

  110. 110 Subscriber Subscriber Publisher Publisher Subscribe 多次

  111. 111 Subscriber Subscriber Publisher Subject Publisher Subscribe 多次

  112. 112 Subscriber Subscriber Publisher Subject Publisher Subscribe 多次

  113. 113 Subscriber Subscriber Publisher Subject Publisher Subscribe 多次

  114. 114 Subscriber Subscriber Publisher Subject Publisher Subscribe 多次

  115. 115 Subscriber Subscriber Publisher Subject Publisher Subscribe 多次

  116. 116 Subscriber Subscriber Publisher Subject Publisher Subscribe 多次

  117. 117 Hot / Cold Publisher

  118. 118

  119. 119 Cold Publisher

  120. Subscriber Publisher 120 Cold Publisher

  121. Subscriber Publisher 121 Cold Publisher

  122. Subscriber Publisher 122 Cold Publisher

  123. Subscriber Publisher 123 Cold Publisher

  124. 124 Hot Publisher

  125. Publisher 125 Hot Publisher

  126. Publisher 126 Hot Publisher

  127. Subscriber Publisher 127 Hot Publisher

  128. Subscriber Publisher 128 Hot Publisher

  129. Subscriber Publisher 129 Hot Publisher

  130. Subscriber Publisher 130 Hot Publisher

  131. 131 Passthrough Subject CurrentValue Subject

  132. 132 Passthrough Subject CurrentValue Subject

  133. 133 Passthrough Subject CurrentValue Subject

  134. 134 Passthrough Subject CurrentValue Subject

  135. 135 Passthrough Subject CurrentValue Subject

  136. 136 Passthrough Subject CurrentValue Subject

  137. 137 let subjectA = PassthroughSubject<Int, Never>() let scanB = subjectA.scan(0,

    +) var receivedC = [Event<Int, Never>]() let sinkC = scanB.sink{ e in receivedC.append(e) } subjectA.send(1...2) var receivedD = [Event<Int, Never>]() let sinkD = scanB.sink{ e in receivedD.append(e) } subjectA.send(3...4) XCTAssertEqual(receivedC, [1, 3, 6, 10]) // 0 + 1...4 XCTAssertEqual(receivedD, [3, 7]) // 0 + 3...4
  138. 138 let subjectA = PassthroughSubject<Int, Never>() let scanB = subjectA.scan(0,

    +) var receivedC = [Event<Int, Never>]() let sinkC = scanB.sink{ e in receivedC.append(e) } subjectA.send(1...2) var receivedD = [Event<Int, Never>]() let sinkD = scanB.sink{ e in receivedD.append(e) } subjectA.send(3...4) XCTAssertEqual(receivedC, [1, 3, 6, 10]) // 0 + 1...4 XCTAssertEqual(receivedD, [3, 7]) // 0 + 3...4
  139. 139 let subjectA = PassthroughSubject<Int, Never>() let scanB = subjectA.scan(0,

    +) var receivedC = [Event<Int, Never>]() let sinkC = scanB.sink{ e in receivedC.append(e) } subjectA.send(1...2) var receivedD = [Event<Int, Never>]() let sinkD = scanB.sink{ e in receivedD.append(e) } subjectA.send(3...4) XCTAssertEqual(receivedC, [1, 3, 6, 10]) // 0 + 1...4 XCTAssertEqual(receivedD, [3, 7]) // 0 + 3...4
  140. FlatMap 140

  141. 之前複習⼀下 向量投影(內積) 141 FlatMap

  142. 142 FlatMap

  143. 143 FlatMap

  144. 144 FlatMap

  145. 145 FlatMap

  146. 146 FlatMap

  147. Error handling By Catch 147 Publisher Subscriber Catch

  148. Error handling By Catch 148 Publisher Subscriber Catch

  149. Error handling By Catch 149 Publisher Subscriber Catch

  150. Error handling By Catch 150 Publisher Subscriber Catch

  151. Error handling By Catch 151 Subscriber Catch

  152. Error handling By Catch 152 Subscriber Catch Recovery Publisher

  153. Error handling By Catch 153 Subscriber Catch Recovery Publisher

  154. Error handling By Catch 154 Subscriber Catch Recovery Publisher

  155. Error From TryMap 155 Publisher Subscriber Catch TryMap

  156. Error From TryMap 156 Publisher Subscriber Catch TryMap

  157. Error From TryMap 157 Publisher Subscriber Catch TryMap

  158. Error From TryMap 158 Publisher Subscriber Catch TryMap

  159. Error From TryMap 159 Publisher Subscriber Catch TryMap

  160. Error From TryMap 160 Publisher Subscriber Catch TryMap

  161. Error From TryMap 161 Subscriber Catch

  162. Error From TryMap 162 Subscriber Catch Recovery Publisher

  163. Error From TryMap 163 Subscriber Catch Recovery Publisher

  164. Error From TryMap 164 Subscriber Catch Recovery Publisher

  165. 使⽤ FlatMap 導流 165 Publisher Subscriber FlatMap

  166. 使⽤ FlatMap 導流 166 Publisher Subscriber FlatMap

  167. 使⽤ FlatMap 導流 167 Publisher Subscriber Catch TryMap FlatMap Catch

    TryMap
  168. 使⽤ FlatMap 導流 168 Publisher Subscriber Catch TryMap FlatMap Catch

    TryMap
  169. 使⽤ FlatMap 導流 169 Publisher Subscriber Catch TryMap FlatMap Catch

    TryMap
  170. 使⽤ FlatMap 導流 170 Publisher Subscriber Catch FlatMap Catch Recovery

    Publisher
  171. 使⽤ FlatMap 導流 171 Publisher Subscriber FlatMap

  172. 使⽤ FlatMap 導流 172 Publisher Subscriber FlatMap

  173. 使⽤ FlatMap 導流 173 Publisher Subscriber FlatMap

  174. 使⽤ FlatMap 導流 174 Publisher Subscriber FlatMap

  175. 使⽤ FlatMap 導流 175 Publisher Subscriber FlatMap Catch TryMap Catch

    TryMap
  176. 使⽤ FlatMap 導流 176 Publisher Subscriber FlatMap Catch TryMap Catch

    TryMap
  177. 使⽤ FlatMap 導流 177 Publisher Subscriber FlatMap

  178. 使⽤ FlatMap 導流 178 Publisher Subscriber FlatMap ✅

  179. 聽完這個 Session 之後呢? 179

  180. Apple/Swift Combine Extension 180 https://github.com/apple/swift/tree/main/stdlib/public/Darwin/Foundation

  181. Reactive Programming Taiwan (RxSwift/Combine) 加入 Telegram 台灣社群 181

  182. 加入 Combine Community 182

  183. 183 watchOS tvOS iOS macOS Combine

  184. 184 watchOS tvOS iOS macOS Linux WASI

  185. Apple Combine Documents WWDC 2019 Session 722, Session 721 cocoawithlove

    by Matt Gallagher - 22 short tests of Combine 
 https://www.cocoawithlove.com/blog/twenty-two-short-tests-of-combine-part-1.html OpenCombine 
 https://github.com/OpenCombine ReactiveX.io 
 http://reactivex.io/ ReactiveCocoa.io 
 https://reactivecocoa.io/ 參考資料 185
  186. 結論 186

  187. 187 Combine 酷嗎?

  188. 188 Combine 酷嗎?

  189. 189 Combine 酷嗎? Combine 好學嗎?

  190. 190 Combine 酷嗎? Combine 好學嗎? ✅

  191. 191 Combine 酷嗎? Combine 好學嗎? ✅ Combine 好⽤嗎?

  192. 192 Combine 酷嗎? Combine 好學嗎? ✅ Combine 好⽤嗎?

  193. 193 ✅ Combine