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

打開 Combine 的引擎蓋看看

Tsungyu Yu
November 08, 2020

打開 Combine 的引擎蓋看看

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

Tsungyu Yu

November 08, 2020
Tweet

More Decks by Tsungyu Yu

Other Decks in Programming

Transcript

  1. 游諭(@ytyubox) 
 ⾃學程式 iOS - 2018 iT 邦幫忙鐵⼈賽: 
 30

    天了解 Swift 的 Combine 
 30 天從 Swift 學會 Objective-C $> whoami 2
  2. 8

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

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

    time. By WWDC 2019: Introducing Combine Functional Reactive Programming Combine
  5. 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
  6. Property Observers ( set / didSet ) Target / Action

    ( @IBAction ) Notification Center Ad-hoc Callbacks 23 asynchronous programing 非同步程式設計 URLSession.dataTask(with:, completionHandler:) Key-Value Observing
  7. 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 複習⼀下
  8. 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 複習⼀下
  9. 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 複習⼀下
  10. 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 複習⼀下
  11. 35 import Combine func greet(person: String) throws -> String {

    let greeting = "Hello, " + person + "!" return greeting }
  12. 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
  13. 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 }
  14. 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>) }
  15. protocol Publisher { ... } protocol Subscriber { ... }

    42 Subscriber Publisher import Combine
  16. 51 Combine Pattern From WWDC 2019 - Introducing Combine 來⾃與

    Introducing Combine https://developer.apple.com/videos/play/wwdc2019/722/
  17. Subscriber Publisher subscribe( ) is attached Publisher Subscriber Subscription receive(subscription:

    ) sends Publisher Subscription Combine Pattern From WWDC 2019 - Introducing Combine 53
  18. 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
  19. 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
  20. 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
  21. 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
  22. 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
  23. client decor 1 driver decor 2 action action action 78

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

    illustrated by Go Naga Has a Man, is a Man. 79 無敵鐵⾦剛
  25. 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
  26. 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
  27. 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
  28. 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
  29. 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
  30. 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
  31. 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
  32. 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
  33. 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
  34. 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
  35. 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
  36. 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
  37. 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
  38. 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
  39. 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
  40. 118

  41. 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
  42. 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
  43. 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
  44. 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