Slide 1

Slide 1 text

RxBlocking Deep Dive Otemachi.swift #2 2018/10/16 @takehilo_kaneko

Slide 2

Slide 2 text

ࣗݾ঺հ ۚࢠ ༤େ @takehilo_kaneko 1೥ऑ https://qiita.com/takehilo

Slide 3

Slide 3 text

͋Μ·Γ໾ʹཱͨͳ͍࿩͠·͢

Slide 4

Slide 4 text

https://speakerdeck.com/takehilo/rxtest-rxblocking-test-patterns

Slide 5

Slide 5 text

https://qiita.com/takehilo/items/09f4a3077e441e5bb9de

Slide 6

Slide 6 text

RxBlockingͬͯͲ͏΍ͬͯ εϨουΛϒϩοΫͯ͠ΔΜͩΖ͏ʁ

Slide 7

Slide 7 text

https://qiita.com/takehilo/items/be30a730599ac0774137 ॻ͖·ͨ͠ʂ

Slide 8

Slide 8 text

RxBlockingͱ͸ʁ

Slide 9

Slide 9 text

ඇಉظίʔυͷςετΛָʹॻͨ͘ΊͷϥΠϒϥϦ • toBlocking() • BlockingObservableܕʹม׵͢Δ • toArray() • ΧϨϯτεϨουΛϒϩοΫ͠ɺObservableΛαϒεΫϥΠϒ͠ɺશ ͯͷΠϕϯτΛड৴ͨ͠Βͦͷશͯͷཁૉฦͯ͠ϒϩοΫΛղআ͢Δ // ඇಉظʹΠϕϯτ͕ൃߦ͞ΕΔObservable let asyncObservable = Observable.of(10, 20, 30) .observeOn(SerialDispatchQueueScheduler(qos: .background)) let elements = try! asyncObservable.toBlocking().toArray() expect(elements).to(equal([10, 20, 30]))

Slide 10

Slide 10 text

ඇಉظίʔυͷςετΛָʹॻͨ͘ΊͷϥΠϒϥϦ • toBlocking() • BlockingObservableܕʹม׵͢Δ • toArray() • ΧϨϯτεϨουΛϒϩοΫ͠ɺObservableΛαϒεΫϥΠϒ͠ɺશ ͯͷΠϕϯτΛड৴ͨ͠Βͦͷશͯͷཁૉฦͯ͠ϒϩοΫΛղআ͢Δ // ඇಉظʹΠϕϯτ͕ൃߦ͞ΕΔObservable let asyncObservable = Observable.of(10, 20, 30) .observeOn(SerialDispatchQueueScheduler(qos: .background)) let elements = try! asyncObservable.toBlocking().toArray() expect(elements).to(equal([10, 20, 30])) ͜͜Λਂ͘۷ͬͯ ͍͚͹Αͦ͞͏ʂ

Slide 11

Slide 11 text

Let's deep dive into RxBlocking!

Slide 12

Slide 12 text

extension BlockingObservable { public func toArray() throws -> [E] { let results = materializeResult() return try elementsOrThrow(results) } } ͞Βʹਂ͘

Slide 13

Slide 13 text

extension BlockingObservable { fileprivate func materializeResult(max: Int? = nil, predicate: @escaping (E) throws -> Bool = { _ in true }) -> MaterializedSequenceResult { var elements: [E] = Array() var error: Swift.Error? let lock = RunLoopLock(timeout: timeout) let d = SingleAssignmentDisposable() defer { d.dispose() } lock.dispatch { let subscription = self.source.subscribe { event in if d.isDisposed { return } switch event { case .next(let element): do { if try predicate(element) { elements.append(element) } if let max = max, elements.count >= max { d.dispose() lock.stop() } } catch (let err) { error = err d.dispose() lock.stop() } case .error(let err): error = err d.dispose() lock.stop() case .completed: d.dispose() lock.stop() } } d.setDisposable(subscription) } do { try lock.run() } catch (let err) { error = err } if let error = error { return MaterializedSequenceResult.failed(elements: elements, error: error) } return MaterializedSequenceResult.completed(elements: elements) } }

Slide 14

Slide 14 text

func materializeResult() -> MaterializedSequenceResult { let lock = RunLoopLock(timeout: timeout) lock.dispatch { /*શͯͷΠϕϯτΛऔಘͨ͠Β࣮ߦϧʔϓΛऴྃ͢Δ*/ } lock.run() return MaterializedSequenceResult.completed(elements: elements) } ௕͍ͷͰϙΠϯτ ͱͳΔ෦෼͚ͩൈਮ

Slide 15

Slide 15 text

func materializeResult() -> MaterializedSequenceResult { let lock = RunLoopLock(timeout: timeout) lock.dispatch { /*શͯͷΠϕϯτΛऔಘͨ͠Β࣮ߦϧʔϓΛऴྃ͢Δ*/ } lock.run() return MaterializedSequenceResult.completed(elements: elements) } ࣮ߦϧʔϓΛ ੍ޚ͢ΔΫϥε

Slide 16

Slide 16 text

func materializeResult() -> MaterializedSequenceResult { let lock = RunLoopLock(timeout: timeout) lock.dispatch { /*શͯͷΠϕϯτΛऔಘͨ͠Β࣮ߦϧʔϓΛऴྃ͢Δ*/ } lock.run() return MaterializedSequenceResult.completed(elements: elements) } ࣮ߦϧʔϓ಺ͷΩϡʔʹ ΫϩʔδϟΛొ࿥

Slide 17

Slide 17 text

func materializeResult() -> MaterializedSequenceResult { let lock = RunLoopLock(timeout: timeout) lock.dispatch { /*શͯͷΠϕϯτΛऔಘͨ͠Β࣮ߦϧʔϓΛऴྃ͢Δ*/ } lock.run() return MaterializedSequenceResult.completed(elements: elements) } ࣮ߦϧʔϓΛ։࢝ ͜͜ͰεϨου͕ϒϩοΫ͞ΕΔ

Slide 18

Slide 18 text

func materializeResult() -> MaterializedSequenceResult { let lock = RunLoopLock(timeout: timeout) lock.dispatch { /*શͯͷΠϕϯτΛऔಘͨ͠Β࣮ߦϧʔϓΛऴྃ͢Δ*/ } lock.run() return MaterializedSequenceResult.completed(elements: elements) } 0CTFSWBCMF͕ൃߦͨ͠ શΠϕϯτΛฦ͢

Slide 19

Slide 19 text

͜ΜͳΠϝʔδ εϨου lock.dispatch {} Ωϡʔ ࣮ߦϧʔϓ lock.run() return start stop process શͯͷΠϕϯτΛऔಘͨ͠Β ࣮ߦϧʔϓΛऴྃ͢Δ

Slide 20

Slide 20 text

RunLoopLockΛਂ۷Γ

Slide 21

Slide 21 text

RunLoopLockΫϥε • ࣮ߦϧʔϓΛ੍ޚ͢ΔػೳΛ࣋ͭ • ࣮ߦϧʔϓΛ੍ޚ͢ΔͨΊͷAPIͰ͋ΔCore FoundationϑϨʔ ϜϫʔΫͷCFRunLoopΛϥοϓͨ͠Ϋϥε

Slide 22

Slide 22 text

func dispatch(_ action: @escaping () -> ()) { CFRunLoopPerformBlock(_currentRunLoop, runLoopModeRaw) { action() } } func stop() { CFRunLoopPerformBlock(_currentRunLoop, runLoopModeRaw) { CFRunLoopStop(self._currentRunLoop) } } func run() throws { CFRunLoopRun() } ͦΕͧΕϙΠϯτ͚ͩൈਮ

Slide 23

Slide 23 text

func dispatch(_ action: @escaping () -> ()) { CFRunLoopPerformBlock(_currentRunLoop, runLoopModeRaw) { action() } } func stop() { CFRunLoopPerformBlock(_currentRunLoop, runLoopModeRaw) { CFRunLoopStop(self._currentRunLoop) } } func run() throws { CFRunLoopRun() } ࣮ߦϧʔϓ಺ͷΩϡʔʹ ΫϩʔδϟΛొ࿥

Slide 24

Slide 24 text

func dispatch(_ action: @escaping () -> ()) { CFRunLoopPerformBlock(_currentRunLoop, runLoopModeRaw) { action() } } func stop() { CFRunLoopPerformBlock(_currentRunLoop, runLoopModeRaw) { CFRunLoopStop(self._currentRunLoop) } } func run() throws { CFRunLoopRun() } ࣮ߦϧʔϓΛऴྃ͢Δ

Slide 25

Slide 25 text

func dispatch(_ action: @escaping () -> ()) { CFRunLoopPerformBlock(_currentRunLoop, runLoopModeRaw) { action() } } func stop() { CFRunLoopPerformBlock(_currentRunLoop, runLoopModeRaw) { CFRunLoopStop(self._currentRunLoop) } } func run() throws { CFRunLoopRun() } ࣮ߦϧʔϓΛ։࢝͢Δ $'3VO-PPQ4UPQ ͕ݺ͹ΕΔ·Ͱ ࣮ߦ͠ଓ͚Δ

Slide 26

Slide 26 text

func dispatch(_ action: @escaping () -> ()) { CFRunLoopPerformBlock(_currentRunLoop, runLoopModeRaw) { action() } } func stop() { CFRunLoopPerformBlock(_currentRunLoop, runLoopModeRaw) { CFRunLoopStop(self._currentRunLoop) } } func run() throws { CFRunLoopRun() } ͍͕ͭ͜εϨουΛϒϩοΫ͢Δ ࢓૊Έͷਖ਼ମʂʂ

Slide 27

Slide 27 text

RxBlockingͬͯͲ͏΍ͬͯ εϨουΛϒϩοΫͯ͠ΔΜͩΖ͏ʁ

Slide 28

Slide 28 text

౴͑ • CFRunLoopRun()ʹΑ࣮ͬͯߦϧʔϓ͕։࢝͞ΕɺεϨου͕ϒ ϩοΫ͞ΕΔ • ࣮ߦϧʔϓ಺ʹͯɺʮObservable͕ൃߦ͢ΔશͯͷΠϕϯτΛ औಘͨ͠Β࣮ߦϧʔϓΛऴྃ͢Δʯͱ͍͏ॲཧΛ࣮ߦ͢Δ

Slide 29

Slide 29 text

5IBOLZPV