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

Integrate Combine into legacy frameworks

Integrate Combine into legacy frameworks

2019/08/05 Combineゴリゴリキャッチアップ会
Twitter hashtag: #combine_meetup

Ryo Aoyama

August 05, 2019
Tweet

More Decks by Ryo Aoyama

Other Decks in Programming

Transcript

  1. PROFILE Ryo Aoyama Cyberagent, Inc Ὂ CATS iOS Lead Ὂ

    WinTicket OSS Author Ὂ DifferenceKit, Carbon, VueFlux, etc… GitHub: @ra1028 Twitter: @ra1028fe5
  2. Publisher KeyValueObserving URLSession NotificationCenter Timer Combine ✕ Foundation TopLevelEncoder /Decoder

    JSONEncoder JSONDecoder PropertyListEncoder PropertyListDecoder Scheduler RunLoop OperationQueue DispatchQueue
  3. Publisher KeyValueObserving URLSession NotificationCenter Timer Combine ✕ Foundation TopLevelEncoder /Decoder

    JSONEncoder JSONDecoder PropertyListEncoder PropertyListDecoder Scheduler RunLoop OperationQueue DispatchQueue
  4. Publisher KeyValueObserving URLSession NotificationCenter Timer Combine ✕ Foundation TopLevelEncoder /Decoder

    JSONEncoder JSONDecoder PropertyListEncoder PropertyListDecoder Scheduler RunLoop OperationQueue DispatchQueue
  5. Publisher KeyValueObserving URLSession NotificationCenter Timer Combine ✕ Foundation TopLevelEncoder /Decoder

    JSONEncoder JSONDecoder PropertyListEncoder PropertyListDecoder Scheduler RunLoop OperationQueue DispatchQueue
  6. N/A

  7. Publisher Declares that a type can transmit a sequence of

    values over time. protocol Publisher { associatedtype Output associatedtype Failure: Error func receive<S>(subscriber: S) where S: Subscriber, Failure == S.Failure, Output == S.Input }
  8. Publisher URLSession extension URLSession { struct DataTaskPublisher: Publisher { typealias

    Output = (data: Data, response: URLResponse) typealias Failure = URLError init(request: URLRequest, session: URLSession) } func dataTaskPublisher(for url: URL) -> DataTaskPublisher }
  9. extension UNUserNotificationCenter { func requestAuthorizationFuture(options: UNAuthorizationOptions) -> Future<Bool, Error> {

    Future { promise in self.requestAuthorization(options: options) { hasAuthorized, error in if let error = error { return promise(.failure(error)) } promise(.success(hasAuthorized)) } } } }
  10. CADisplayLink A timer object that allows your application to synchronize

    its drawing to the refresh rate of the display. class CADisplayLink: NSObject { var preferredFramesPerSecond: Int init(target: Any, selector sel: Selector) func add(to runloop: RunLoop, forMode mode: RunLoop.Mode) func invalidate() }
  11. extension CADisplayLink { struct Publisher: Combine.Publisher { typealias Output =

    CADisplayLink typealias Failure = Never var runLoop: RunLoop var mode: RunLoop.Mode var preferredFramesPerSecond: Int func receive<S: Subscriber>(subscriber: S) where Failure == S.Failure, Output == S.Input { #warning("TODO:") } } }
  12. ※ initলུ extension CADisplayLink { final class Subscription<S: Subscriber>: Combine.Subscription

    where S.Input == CADisplayLink { let combineIdentifier = CombineIdentifier() let subscriber: S let runLoop: RunLoop let mode: RunLoop.Mode let preferredFramesPerSecond: Int private var displayLink: CADisplayLink? @objc func receive(sender: CADisplayLink) { _ = subscriber.receive(sender) } func request(_ demand: Subscribers.Demand) { let displayLink = CADisplayLink(target: self, selector: #selector(receive(sender:))) displayLink.add(to: runLoop, forMode: mode) displayLink.preferredFramesPerSecond = preferredFramesPerSecond self.displayLink = displayLink } func cancel() { displayLink?.invalidate() displayLink = nil } } }
  13. extension CADisplayLink { final class Subscription<S: Subscriber>: Combine.Subscription where S.Input

    == CADisplayLink { let combineIdentifier = CombineIdentifier() let subscriber: S let runLoop: RunLoop let mode: RunLoop.Mode let preferredFramesPerSecond: Int private var displayLink: CADisplayLink? @objc func receive(sender: CADisplayLink) { _ = subscriber.receive(sender) } func request(_ demand: Subscribers.Demand) { let displayLink = CADisplayLink(target: self, selector: #selector(receive(sender:))) displayLink.add(to: runLoop, forMode: mode) displayLink.preferredFramesPerSecond = preferredFramesPerSecond self.displayLink = displayLink } func cancel() { displayLink?.invalidate() displayLink = nil } } } ※ initলུ Subscriptionͷࣝผࢠ
  14. extension CADisplayLink { final class Subscription<S: Subscriber>: Combine.Subscription where S.Input

    == CADisplayLink { let combineIdentifier = CombineIdentifier() let subscriber: S let runLoop: RunLoop let mode: RunLoop.Mode let preferredFramesPerSecond: Int private var displayLink: CADisplayLink? @objc func receive(sender: CADisplayLink) { _ = subscriber.receive(sender) } func request(_ demand: Subscribers.Demand) { let displayLink = CADisplayLink(target: self, selector: #selector(receive(sender:))) displayLink.add(to: runLoop, forMode: mode) displayLink.preferredFramesPerSecond = preferredFramesPerSecond self.displayLink = displayLink } func cancel() { displayLink?.invalidate() displayLink = nil } } } ※ initলུ Subscriptionଆ͔Β஋Λྲྀͨ͢ΊsubscriberΛ౉͢
  15. extension CADisplayLink { final class Subscription<S: Subscriber>: Combine.Subscription where S.Input

    == CADisplayLink { let combineIdentifier = CombineIdentifier() let subscriber: S let runLoop: RunLoop let mode: RunLoop.Mode let preferredFramesPerSecond: Int private var displayLink: CADisplayLink? @objc func receive(sender: CADisplayLink) { _ = subscriber.receive(sender) } func request(_ demand: Subscribers.Demand) { let displayLink = CADisplayLink(target: self, selector: #selector(receive(sender:))) displayLink.add(to: runLoop, forMode: mode) displayLink.preferredFramesPerSecond = preferredFramesPerSecond self.displayLink = displayLink } func cancel() { displayLink?.invalidate() displayLink = nil } } } ※ initলུ ड͚ͨCADisplayLinkΛsubscriberʹྲྀ͢selector༻ͷmethod
  16. extension CADisplayLink { final class Subscription<S: Subscriber>: Combine.Subscription where S.Input

    == CADisplayLink { let combineIdentifier = CombineIdentifier() let subscriber: S let runLoop: RunLoop let mode: RunLoop.Mode let preferredFramesPerSecond: Int private var displayLink: CADisplayLink? @objc func receive(sender: CADisplayLink) { _ = subscriber.receive(sender) } func request(_ demand: Subscribers.Demand) { let displayLink = CADisplayLink(target: self, selector: #selector(receive(sender:))) displayLink.add(to: runLoop, forMode: mode) displayLink.preferredFramesPerSecond = preferredFramesPerSecond self.displayLink = displayLink } func cancel() { displayLink?.invalidate() displayLink = nil } } } ※ initলུ ඞཁʹԠͯ͡BackpressureʹରԠͤ͞Δ requestͰCADisplayLinkΛ࣮ߦ
  17. extension CADisplayLink { final class Subscription<S: Subscriber>: Combine.Subscription where S.Input

    == CADisplayLink { let combineIdentifier = CombineIdentifier() let subscriber: S let runLoop: RunLoop let mode: RunLoop.Mode let preferredFramesPerSecond: Int private var displayLink: CADisplayLink? @objc func receive(sender: CADisplayLink) { _ = subscriber.receive(sender) } func request(_ demand: Subscribers.Demand) { let displayLink = CADisplayLink(target: self, selector: #selector(receive(sender:))) displayLink.add(to: runLoop, forMode: mode) displayLink.preferredFramesPerSecond = preferredFramesPerSecond self.displayLink = displayLink } func cancel() { displayLink?.invalidate() displayLink = nil } } } ※ initলུ ߪಡଆͰΩϟϯηϧ͞Εͨͱ͖ͷڍಈ
  18. extension CADisplayLink { struct Publisher: Combine.Publisher { typealias Output =

    CADisplayLink typealias Failure = Never var runLoop: RunLoop var mode: RunLoop.Mode var preferredFramesPerSecond: Int func receive<S: Subscriber>(subscriber: S) where Failure == S.Failure, Output == S.Input { let subscription = Subscription( subscriber: subscriber, runLoop: runLoop, mode: mode, preferredFramesPerSecond: preferredFramesPerSecond ) subscriber.receive(subscription: subscription) } } }
  19. extension CADisplayLink { static func publish(on runLoop: RunLoop, forMode mode:

    RunLoop.Mode, preferredFramesPerSecond: Int) -> Publisher { Publisher(runLoop: runLoop, mode: mode, preferredFramesPerSecond: preferredFramesPerSecond) } }
  20. 173153.771382711 173153.78804937802 173153.804716045 173153.82138271202 173153.838049379 173153.85471604601 . . . let

    cancellable = CADisplayLink.publish( on: .main, forMode: .default, preferredFramesPerSecond: 60 ) .sink(receiveValue: { print($0.timestamp) }) self.cancellable = AnyCancellable(cancellable) AnyCancellable͸deinit࣌ʹࣗಈcancel͞ΕΔͷͰ஫ҙ