Combine Overview

2594ac7ce91fd7d9a3ce71ca7cc2d0c0?s=47 d_date
August 05, 2019

Combine Overview

2019/08/05 Combineゴリゴリキャッチアップ会

2594ac7ce91fd7d9a3ce71ca7cc2d0c0?s=128

d_date

August 05, 2019
Tweet

Transcript

  1. Combine Overview CombineΰϦΰϦΩϟονΞοϓձ Daiki Matsudate @d_date iOS Developer

  2. Daiki Matsudate • Tokyo, Japan • iOS Developer from iOS

    4 • Independent Developer • @d_date • Sushi
  3. March, 18 - 20th, 2020 https://www.tryswift.co/

  4. A unified, declarative API for 
 processing values over time

  5. Why Reactive Programing?

  6. Why Reactive Programing? • ෳ਺ͷඇಉظ (Asynchronous) ͳλεΫΛͲ͏ѻ͏͔ • Ͳ͏ѻ͏͔ •

    Ͳͷॱ൪Ͱʁ • ௚ྻ / ฒྻ? • ్தͰΤϥʔ͕ൃੜͨ͠Βʁ
  7. Reactive Programing

  8. Reactive Programming • Observer Pattern • ΠϕϯτͷൃՐΛϋϯυϦϯά͍ͨ͠ͱ͜ΖͰ؂ࢹ͢Δ • Operators •

    ΠϕϯτΛՃ޻͢Δ • Open Source • RxSwift / Reactive Swift
  9. https://twitter.com/diegopetrucci/status/1135655480825655297

  10. Combine

  11. Combine • Declarative Swift API • ඇಉظͳΠϕϯτΛܕͱͯ͠දݱ • ଟछଟ༷ͳԋࢉࢠͰΠϕϯτΛϋϯυϦϯά •

    Reactive Framework by Apple
  12. Publisher

  13. Publisher • Value΍ErrorΛੜ੒͢Δ • ొ࿥͞ΕͨSubscriberʹValue΍ErrorΛૹ৴͢Δ

  14. public protocol Publisher { /// The kind of values published

    by this publisher. associatedtype Output /// The kind of errors this publisher might publish. /// Use `Never` if this `Publisher` does not publish errors. associatedtype Failure : Error /// This function is called to attach the specified `Subscriber` to this `Publisher` by `subscribe(_:)` func receive<S>(subscriber: S) where S : Subscriber, Self.Failure == S.Failure, Self.Output == S.Input } Publisher
  15. @Published @propertyWrapper public struct Published<Value> { /// Initialize the storage

    of the Published property as well as the corresponding `Publisher`. public init(initialValue: Value) public class Publisher : Publisher { /// The kind of values published by this publisher. public typealias Output = Value /// The kind of errors this publisher might publish. /// /// Use `Never` if this `Publisher` does not publish errors. public typealias Failure = Never /// This function is called to attach the specified `Subscriber` to this `Publisher` by `subscribe(_:)` public func receive<S>(subscriber: S) where Value == S.Input, S : Subscriber, S.Failure == Published<Value>.Publisher.Failure } /// The property that can be accessed with the `$` syntax and allows access to the `Publisher` public var projectedValue: Published<Value>.Publisher { mutating get } }
  16. Default Publishers • Notification Center • URLSession • Just, Future

    • CurrentValueSubject, PassthroughSubject • NSManagedObject let ncpublisher = NotificationCenter.Publisher(center: .default, name: .NSCalendarDayChanged) let sessionPublisher = URLSession(configuration: .default) .dataTaskPublisher(for: URL(string: “https://api.google.com")!) let managedObject = NSManagedObject(context: context).publisher(for: \.hasChanges) let just = Just<String>("") let future = Future<String, Error> { (result) in result(.success("result")) result(.failure(TypedError.example)) }
  17. From WWDC19 Introducing Combine One Many Synchronous Asynchronous Int Array

    Future Publisher
  18. Subscriber

  19. Publisher • Publisher͕ൃߦͨ͠஋΍Error, completionΛߪಡ͢Δ • ߪಡΛΩϟϯηϧͰ͖ΔʢCancellable)

  20. public protocol Subscriber : CustomCombineIdentifierConvertible { /// The kind of

    values this subscriber receives. associatedtype Input /// The kind of errors this subscriber might receive. /// Use `Never` if this `Subscriber` cannot receive errors. associatedtype Failure : Error /// Tells the subscriber that it has successfully subscribed to the publisher and may request items. /// /// Use the received `Subscription` to request items from the publisher. func receive(subscription: Subscription) /// Tells the subscriber that the publisher has produced an element. func receive(_ input: Self.Input) -> Subscribers.Demand /// Tells the subscriber that the publisher has completed publishing, either normally or with an error. func receive(completion: Subscribers.Completion<Self.Failure>) } Subscriber
  21. Assign

  22. let labelTextSubscriber = Subscribers.Assign(object: label, keyPath: \.text) $queryPublisher .map {

    "searching for \($0)..." } .map(Optional.init) .subscribe(labelTextSubscriber) labelTextSubscriber.cancel() Assign
  23. let cancellable = $queryPublisher .map { "searching for \($0)..." }

    .assign(to: \.text, on: label) // how to cancel cancellable.cancel() Assign
  24. var cancellables = [AnyCancellable]() $queryPublisher .map { "searching for \($0)..."

    } .assign(to: \.text, on: label) .store(in: &cancellables) Assign
  25. Sink

  26. let labelTextSubscriber = Subscribers.Sink<String?, Never>(receiveCompletion: { _ in }) {

    self.label.text = $0 } $queryPublisher .map { "searching for \($0)..." } .subscribe(labelTextSubscriber) labelTextSubscriber.cancel() Sink
  27. let cancellable = $queryPublisher .map { "searching for \($0)..." }

    .sink { self.label.text = $0 } cancellable.cancel() Sink
  28. var cancellables = [AnyCancellable]() $queryPublisher .map { "searching for \($0)..."

    } .sink { self.label.text = $0 } .store(in: &cancellables) Sink
  29. subscribe(on:) vs. receive(on:)

  30. subscribe(on:) vs. receive(on:) • subscribe(on:) ͸ upstream messagesʹӨڹ • receive(on:)

    ͸downstream messagesͷ࣮ߦίϯςΩετʹӨڹ • ͜ͷྫͰ͸ɺpublisher΁ͷϦΫΤετ͸backgroundQueueͰߦΘΕΔ ͕ɺΤϨϝϯτΛड͚औΔͷ͸ϝΠϯεϨου let backgroundQueue = DispatchQueue(label: "background", qos: .utility) $queryPublisher .map { "searching for \($0)..." } .subscribe(on: backgroundQueue) .receive(on: DispatchQueue.main) .sink { self.label.text = $0 } .store(in: &cancellables)
  31. The Pattern Subscriber is attached to Publisher Publisher sends a

    Subscription Subscriber requests N values Publisher sends N values or less Publisher sends completion Publisher Subscriber subscribe( ) receive(subscription:) request(_ : Demand) receive(completion:) receive(_ : Input) receive(_ : Input) • • •
  32. Subject

  33. Subject • PublisherͱSubscriberͷ྆ํͷੑ࣭Λ΋ͭ • ෳ਺ͷSubscriber΁஋ΛϒϩʔυΩϟετ͢Δ

  34. Kinds of Subjects Passthrough CurrentValue

  35. Kinds of Subjects Passthrough CurrentValue

  36. Binding to SwiftUI

  37. // Combine with SwiftUI class WizardModel : BindableObject { var

    trick: WizardTrick { didSet { didChange.send() } var wand: Wand? { didSet { didChange.send() } let didChange = PassthroughSubject<Void, Never>() } struct TrickView: View { @ObjectBinding var model: WizardModel var body: some View { Text(model.trick.name) } }
  38. Operator

  39. catch abortOnError allSatisfy append breakpoint breakpointOnError combineLatest compactMap count dropFirst

    filter first handleEvents ignoreOutput last log mapError max min output prefix prepend print removeDuplicates replaceEmpty replaceError replaceNil retry scan setFailureType switchToLatest zip map contains flatMap reduce merge drop collect From WWDC19 Introducing Combine
  40. Error Handling

  41. Failure Handling Operators assertNoFailure retry catch mapError setFailureType

  42. https://github.com/freak4pc/rxswift-to-combine-cheatsheet/blob/master/Data/core_components.csv RxSwift to Combine

  43. WWDC Sessions Introducing Combine WWDC19 Combine in Practice WWDC19 Data

    Flow through SwiftUI WWDC19
  44. Great Presentations • https://speakerdeck.com/riteshhh/combine-all-the-things