Slide 1

Slide 1 text

Integrate Combine into legacy frameworks 2019.08.05 MON 
 CombineΰϦΰϦΩϟονΞοϓձ #combine_meetup Ryo Aoyama GitHub: @ra1028 Twitter: @ra1028fe5

Slide 2

Slide 2 text

PROFILE Ryo Aoyama Cyberagent, Inc Ὂ CATS iOS Lead Ὂ WinTicket OSS Author Ὂ DifferenceKit, Carbon, VueFlux, etc… GitHub: @ra1028 Twitter: @ra1028fe5

Slide 3

Slide 3 text

Combine has released

Slide 4

Slide 4 text

Current Status: iOS 13 beta 5 Xcode 11 beta 5

Slide 5

Slide 5 text

ࠓ೔ͷ࿩୊

Slide 6

Slide 6 text

Integrate Combine into legacy frameworks

Slide 7

Slide 7 text

Combine ✕ Foundation

Slide 8

Slide 8 text

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

Slide 9

Slide 9 text

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

Slide 10

Slide 10 text

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

Slide 11

Slide 11 text

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

Slide 12

Slide 12 text

Combine ✕ UIKit

Slide 13

Slide 13 text

N/A

Slide 14

Slide 14 text

طଘFrameworkͱCombineΛ౷߹͍ͯ͘͠ʹ͸ʁ

Slide 15

Slide 15 text

Publisher Declares that a type can transmit a sequence of values over time. protocol Publisher { associatedtype Output associatedtype Failure: Error func receive(subscriber: S) where S: Subscriber, Failure == S.Failure, Output == S.Input }

Slide 16

Slide 16 text

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 }

Slide 17

Slide 17 text

֤Publisher͸ͦΕͧΕͷৼΔ෣͍ʹରͯ͠ ઐ༻ͷܕΛఆ͍ٛͯ͠Δ

Slide 18

Slide 18 text

RxSwift.Observable.create͸?

Slide 19

Slide 19 text

Ωϟϯηϧ͠ͳ͍ඇಉظॲཧͳΒFuture͕ར༻Ͱ͖Δ

Slide 20

Slide 20 text

extension UNUserNotificationCenter { func requestAuthorizationFuture(options: UNAuthorizationOptions) -> Future { Future { promise in self.requestAuthorization(options: options) { hasAuthorized, error in if let error = error { return promise(.failure(error)) } promise(.success(hasAuthorized)) } } } }

Slide 21

Slide 21 text

ͨͩ͠ΩϟϯηϧෆՄ Hotͳੑ࣭Λ΋ͪɺFutureΛΠϯελϯεԽ ͨ࣌͠఺Ͱ࣮ߦ͞ΕΔ͜ͱʹ஫ҙ

Slide 22

Slide 22 text

AnyPublisher.init AnyPublisher { subscriber in _ = subscriber.receive(value) subscriber.receive(completion: .finished) } Xcode Beta 3

Slide 23

Slide 23 text

AnyPublisher.init AnyPublisher { subscriber in _ = subscriber.receive(value) subscriber.receive(completion: .finished) } Xcode Beta 4+

Slide 24

Slide 24 text

ObservableͷΑ͏ͳ൚༻ܕΛଟ໨తʹ࢖͏Α͏ͳ API͸ఏڙ͞Ε͍ͯͳ͍ ͋͘·ͰطଘͷPublisherͷcomposition͔ ઐ༻ͷܕΛఆٛ͢Δ͜ͱΛਪ঑͍ͯ͠ΔΑ͏

Slide 25

Slide 25 text

The Pattern of Combine Source: https://developer.apple.com/videos/play/wwdc2019/722/

Slide 26

Slide 26 text

Publisher Subscription Subscriber

Slide 27

Slide 27 text

ྫͱͯ͠ CADisplayLink + Publisher Λ࣮૷ͯ͠ΈΔ

Slide 28

Slide 28 text

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() }

Slide 29

Slide 29 text

No content

Slide 30

Slide 30 text

extension CADisplayLink { struct Publisher: Combine.Publisher { typealias Output = CADisplayLink typealias Failure = Never var runLoop: RunLoop var mode: RunLoop.Mode var preferredFramesPerSecond: Int func receive(subscriber: S) where Failure == S.Failure, Output == S.Input { #warning("TODO:") } } }

Slide 31

Slide 31 text

No content

Slide 32

Slide 32 text

※ initলུ extension CADisplayLink { final class Subscription: 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 } } }

Slide 33

Slide 33 text

extension CADisplayLink { final class Subscription: 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ͷࣝผࢠ

Slide 34

Slide 34 text

extension CADisplayLink { final class Subscription: 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Λ౉͢

Slide 35

Slide 35 text

extension CADisplayLink { final class Subscription: 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

Slide 36

Slide 36 text

extension CADisplayLink { final class Subscription: 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Λ࣮ߦ

Slide 37

Slide 37 text

extension CADisplayLink { final class Subscription: 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লུ ߪಡଆͰΩϟϯηϧ͞Εͨͱ͖ͷڍಈ

Slide 38

Slide 38 text

extension CADisplayLink { struct Publisher: Combine.Publisher { typealias Output = CADisplayLink typealias Failure = Never var runLoop: RunLoop var mode: RunLoop.Mode var preferredFramesPerSecond: Int func receive(subscriber: S) where Failure == S.Failure, Output == S.Input { let subscription = Subscription( subscriber: subscriber, runLoop: runLoop, mode: mode, preferredFramesPerSecond: preferredFramesPerSecond ) subscriber.receive(subscription: subscription) } } }

Slide 39

Slide 39 text

ࠓճ͸׬ྃΠϕϯτ͕ͳ͍ͷͰলུ

Slide 40

Slide 40 text

extension CADisplayLink { static func publish(on runLoop: RunLoop, forMode mode: RunLoop.Mode, preferredFramesPerSecond: Int) -> Publisher { Publisher(runLoop: runLoop, mode: mode, preferredFramesPerSecond: preferredFramesPerSecond) } }

Slide 41

Slide 41 text

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͞ΕΔͷͰ஫ҙ

Slide 42

Slide 42 text

ɾbuilt-inͰఏڙ͞ΕΔͷ͸FoundationͷҰ෦APIͷΈ ※1 ɾPublisher͸ઐ༻ܕΛఆٛͨ͠΄͏͕Αͦ͞͏ ※1 RealityKitͳͲ΋Ұ෼ରԠ ɾͳͷͰUIKitͳͲطଘͷFramework͸ࣗલͰ౷߹ͤ͞Δ ɾͱ͸͍͑ຖճPublisherɺSubscriptionΛఆٛ͢Δͷ͸໘౗ ·ͱΊ

Slide 43

Slide 43 text

Thanks