Slide 1

Slide 1 text

Copyright © 2023 treastrain / Tanaka RyogaɹAll rights reserved. USFBTUSBJO5BOBLB3ZPHB%F/"$P -UE $PNCJOFɾsinkʢ3Y4XJGUɾsubscribeʣͰ͸ 
 TaskΛ࡞Βͳ͍Α͏ʹͯ͠ 
 asyncͳϝιουΛݺͿ 4XJGU8FEOFTEBZ 1 ެ։4XJGU8FEOFTEBZʲJ04%$+BQBO௚લʳ"VHVTU

Slide 2

Slide 2 text

Copyright © 2023 treastrain / Tanaka RyogaɹAll rights reserved. ඇಉظॲཧͷҠߦ $PNCJOFɾ3Y4XJGUˠ4XJGU$PODVSSFODZ w ͭͷΠϕϯτ w $PNCJOFFutureʢDeferredFutureʣ w 3Y4XJGUSingleɾMaybeɾCompletable w ෳ਺ͷΠϕϯτ w $PNCJOFPublisher w 3Y4XJGURxSwift.Observable 2

Slide 3

Slide 3 text

Copyright © 2023 treastrain / Tanaka RyogaɹAll rights reserved. ݺΜͰ͍ͨϝιου͕asyncʹͳͬͨ 4XJGU$PODVSSFODZ΁ͷҠߦ 3 import Combine import RxSwift import UIKit protocol PresenterProtocol { func handleEventA() func handleEventB() } final class ViewController: UIViewController { let presenter: some PresenterProtocol = ... // Combine let publisher: some Publisher = ... var cancellables: [AnyCancellable] = [] // RxSwift let observable: RxSwift.Observable = ... let disposeBag = DisposeBag() override func viewDidLoad() { super.viewDidLoad() connect() } } extension ViewController { func connect() { publisher .sink( receiveValue: { [weak self] output in self?.presenter.handleEventA() } ) .store(in: &cancellables) observable .subscribe( onNext: { [weak self] output in self?.presenter.handleEventB() } ) .disposed(by: disposeBag) } } async async

Slide 4

Slide 4 text

Copyright © 2023 treastrain / Tanaka RyogaɹAll rights reserved. ݺΜͰ͍ͨϝιου͕asyncʹͳͬͨ 4XJGU$PODVSSFODZ΁ͷҠߦ 4 import Combine import RxSwift import UIKit protocol PresenterProtocol { func handleEventA() async func handleEventB() async } final class ViewController: UIViewController { let presenter: some PresenterProtocol = ... // Combine let publisher: some Publisher = ... var cancellables: [AnyCancellable] = [] // RxSwift let observable: RxSwift.Observable = ... let disposeBag = DisposeBag() override func viewDidLoad() { super.viewDidLoad() connect() } } extension ViewController { func connect() { publisher .sink( receiveValue: { [weak self] output in self?.presenter.handleEventA() } ) .store(in: &cancellables) observable .subscribe( onNext: { [weak self] output in self?.presenter.handleEventB() } ) .disposed(by: disposeBag) } } 'async' call in a function that does not support concurrency

Slide 5

Slide 5 text

Copyright © 2023 treastrain / Tanaka RyogaɹAll rights reserved. ҟͳΔΞΫλʔͷϝιουΛݺͿ͜ͱʹͳͬͨ 4XJGU$PODVSSFODZ΁ͷҠߦ 5 import Combine import RxSwift import UIKit protocol ViewProtocol: AnyObject { func handleEventA() func handleEventB() } final class ViewController: UIViewController { @ViewLoading var object: ViewObject override func viewDidLoad() { super.viewDidLoad() object = .init(view: self) } } extension ViewController: ViewProtocol { ... } final class ViewObject { unowned let view: View ... func connect() { publisher .sink( receiveValue: { [weak self] output in self?.view.handleEventA() } ) .store(in: &cancellables) observable .subscribe( onNext: { [weak self] output in self?.view.handleEventB() } ) .disposed(by: disposeBag) } } @MainActor

Slide 6

Slide 6 text

Copyright © 2023 treastrain / Tanaka RyogaɹAll rights reserved. ҟͳΔΞΫλʔͷϝιουΛݺͿ͜ͱʹͳͬͨ 4XJGU$PODVSSFODZ΁ͷҠߦ 6 import Combine import RxSwift import UIKit @MainActor protocol ViewProtocol: AnyObject { func handleEventA() func handleEventB() } final class ViewController: UIViewController { @ViewLoading var object: ViewObject override func viewDidLoad() { super.viewDidLoad() object = .init(view: self) } } extension ViewController: ViewProtocol { ... } final class ViewObject { unowned let view: View ... func connect() { publisher .sink( receiveValue: { [weak self] output in self?.view.handleEventA() } ) .store(in: &cancellables) observable .subscribe( onNext: { [weak self] output in self?.view.handleEventB() } ) .disposed(by: disposeBag) } } Call to main actor-isolated instance method * in a synchronous nonisolated context

Slide 7

Slide 7 text

Copyright © 2023 treastrain / Tanaka RyogaɹAll rights reserved. 4XJGU$PODVSSFODZ΁ͷҠߦ ίϯύΠϧ࣌ͷΤϥʔΛղফ͢Δʹ͸ w Πϕϯτड৴࣌ʹasyncͳ 
 ϝιουΛ࣮ߦͨ͘͠ͳͬͨ 㾎TaskΛ࡞ͬͯݺͿ w Πϕϯτड৴࣌ʹ 
 ҟͳΔΞΫλʔʹ෼཭͞Ε͍ͯΔ 
 ϝιουΛ࣮ߦͨ͘͠ͳͬͨ 㾎TaskΛ࡞ͬͯݺͿ 㾎ಉ͡ΞΫλʔͰ࣮ߦ͢ΔΑ͏ʹ ݺͿ 7

Slide 8

Slide 8 text

Copyright © 2023 treastrain / Tanaka RyogaɹAll rights reserved. Πϕϯτड৴৔ॴͰTaskΛ࡞ͬͯawait asyncͳϝιουΛݺͿͱ͖ 8 extension ViewController { func connect() { publisher .sink( receiveValue: { [weak self] output in self?.presenter.handleEventA() } ) .store(in: &cancellables) observable .subscribe( onNext: { [weak self] output in self?.presenter.handleEventB() } ) .disposed(by: disposeBag) } } extension ViewController { func connect() { publisher .sink( receiveValue: { [weak self] output in Task { await self?.presenter.handleEventA() } } ) .store(in: &cancellables) observable .subscribe( onNext: { [weak self] output in Task { await self?.presenter.handleEventB() } } ) .disposed(by: disposeBag) } }

Slide 9

Slide 9 text

Copyright © 2023 treastrain / Tanaka RyogaɹAll rights reserved. Πϕϯτड৴৔ॴͰTaskΛ࡞ͬͯawait asyncͳϝιουΛݺͿͱ͖ w self͸͜ͷ··Ͱ͍͍ͷ͔ʜʜʁ w ࣮ߦ͞ΕͨTaskͷΩϟϯηϧΛ Ͳ͏͠Α͏ʜʜ w $PNCJOFɾ3Y4XJGUͷ 
 4DIFEVMFSͷࢦఆ͸ʜʜʁ w ωετ͕ਂ͍ʜʜ 9 extension ViewController { func connect() { publisher .sink( receiveValue: { [weak self] output in Task { await self?.presenter.handleEventA() } } ) .store(in: &cancellables) observable .subscribe( onNext: { [weak self] output in Task { await self?.presenter.handleEventB() } } ) .disposed(by: disposeBag) } }

Slide 10

Slide 10 text

Copyright © 2023 treastrain / Tanaka RyogaɹAll rights reserved. ΠϕϯτΛ 
 AsyncSequenceͰड৴͢Δ 10

Slide 11

Slide 11 text

Copyright © 2023 treastrain / Tanaka RyogaɹAll rights reserved. AsyncSequence ͓͞Β͍ w Sequenceͷඇಉظ൛ w for-inϧʔϓΛॻ͚Δ 11 let numbers: some Sequence = [0, 1, 2, 3] for number in numbers { print(number, terminator: " ") } // "0 1 2 3 " var iterator = [0, 1, 2, 3].makeIterator() let counter: some AsyncSequence = AsyncStream { try? await Task.sleep(for: .seconds(1)) return iterator.next() } for try await count in counter { print(count, terminator: " ") } // "0 1 2 3 "

Slide 12

Slide 12 text

Copyright © 2023 treastrain / Tanaka RyogaɹAll rights reserved. AsyncSequence ͓͞Β͍ w Sequenceͷඇಉظ൛ w for-inϧʔϓΛॻ͚Δ w for-await-inϧʔϓΛॻ͚Δ w ࣦഊ͢ΔՄೳੑ͕͋ΔͳΒ 
 for-try-await-inϧʔϓ 12 let numbers: some Sequence = [0, 1, 2, 3] for number in numbers { print(number, terminator: " ") } // "0 1 2 3 " var iterator = [0, 1, 2, 3].makeIterator() let counter: some AsyncSequence = AsyncStream { try? await Task.sleep(for: .seconds(1)) return iterator.next() } for try await count in counter { print(count, terminator: " ") } // "0 1 2 3 "

Slide 13

Slide 13 text

Copyright © 2023 treastrain / Tanaka RyogaɹAll rights reserved. ༻ҙ͞Ε͍ͯΔΠϯελϯεϓϩύςΟ ͍ͣΕ΋AsyncSequenceʹద߹͍ͯ͠Δ w $PNCJOF w 3Y4XJGUʢ3Y4XJGUʣ 13 extension Publisher where Self.Failure == Never { @available(macOS 12.0, iOS 15.0, tvOS 15.0, watchOS 8.0, *) public var values: AsyncPublisher { get } } extension Publisher { @available(macOS 12.0, iOS 15.0, tvOS 15.0, watchOS 8.0, *) public var values: AsyncThrowingPublisher { get } } #if swift(>=5.5.2) && canImport(_Concurrency) import Foundation @available(macOS 10.15, iOS 13.0, watchOS 6.0, tvOS 13.0, *) public extension ObservableConvertibleType { var values: AsyncThrowingStream { get } } #endif #if swift(>=5.5.2) && canImport(_Concurrency) && !os(Linux) @available(macOS 10.15, iOS 13.0, watchOS 6.0, tvOS 13.0, *) public extension InfallibleType { var values: AsyncStream { get } } #endif

Slide 14

Slide 14 text

Copyright © 2023 treastrain / Tanaka RyogaɹAll rights reserved. valuesΛ࢖ͬͯfor-await-inϧʔϓͰड৴ await͕࢖͑ͯίʔυ͕୹͘ͳΓωετ΋ઙ͘ͳͬͨ 14 extension ViewController { func connect() { publisher .sink( receiveValue: { [weak self] output in Task { await self?.presenter.handleEventA() } } ) .store(in: &cancellables) observable .subscribe( onNext: { [weak self] output in Task { await self?.presenter.handleEventB() } } ) .disposed(by: disposeBag) } } extension ViewController { func connect() { Task { for await output in publisher.values { await presenter.handleEventA() } } Task { for try await output in observable.values { await presenter.handleEventB() } } } }

Slide 15

Slide 15 text

Copyright © 2023 treastrain / Tanaka RyogaɹAll rights reserved. valuesΛ࢖ͬͯfor-await-inϧʔϓͰड৴ await͕࢖͑ͯίʔυ͕୹͘ͳΓωετ΋ઙ͘ͳͬͨ 15 extension ViewController { func connect() { publisher .sink( receiveValue: { [weak self] output in Task { await self?.presenter.handleEventA() } } ) .store(in: &cancellables) observable .subscribe( onNext: { [weak self] output in Task { await self?.presenter.handleEventB() } } ) .disposed(by: disposeBag) } } extension ViewController { func connect() { Task { for await output in publisher.values { await presenter.handleEventA() } } Task { for try await output in observable.values { await presenter.handleEventB() } } } } 🎉

Slide 16

Slide 16 text

Copyright © 2023 treastrain / Tanaka RyogaɹAll rights reserved. ࣮ࡍʹಈ͔ͯ͠ΈΔ for-await-inϧʔϓͰड৴ 16 import Combine import UIKit final class ViewController: UIViewController { deinit { print(self, "is deinited 🎉") } override func viewDidLoad() { // ... connect() } let publisher: some Publisher = ... func connect() { Task { for await output in publisher.values { // ... } } } } EFJOJU͕ݺ͹Εͳ͍ʜʜ GPSBXBJUJOϧʔϓΛ 
 ऴྃͤ͞Δज़͕ͳ͍

Slide 17

Slide 17 text

Copyright © 2023 treastrain / Tanaka RyogaɹAll rights reserved. handleEventsɾreceiveCancel͕ݺ͹Εͳ͍ 3Y4XJGUͳΒdoɾonDispose 17 import Combine import UIKit final class ViewController: UIViewController { // ... func connect() { Task { let values = publisher .handleEvents( receiveCancel: { print("receiveCancel") } ) .values for await output in values { // ... } } } // ... } ݺ͹Εͳ͍ʜʜ Ωϟϯηϧ͞Εͣʹ଴͍ͬͯΔ

Slide 18

Slide 18 text

Copyright © 2023 treastrain / Tanaka RyogaɹAll rights reserved. handleEventsɾreceiveCancel͕ݺ͹Εͳ͍ 3Y4XJGUͳΒdoɾonDispose 18 import Combine import UIKit final class ViewController: UIViewController { // ... func connect() { Task { let values = publisher .handleEvents( receiveCancel: { print("receiveCancel") } ) .values for await output in values { // ... } } } // ... } import Combine import UIKit final class ViewController: UIViewController { // ... private var cancellables: [AnyCancellable] = [] func connect() { publisher .handleEvents( receiveCancel: { print("receiveCancel") } ) .sink( receiveValue: { [weak self] output in // ... } ) .store(in: &cancellables) } // ... } ݺ͹Εͳ͍ʜʜ Ωϟϯηϧ͞Εͣʹ଴͍ͬͯΔ ݺ͹ΕΔʂ

Slide 19

Slide 19 text

Copyright © 2023 treastrain / Tanaka RyogaɹAll rights reserved. for-await-inͷ͋ΔTaskʹ 
 ΩϟϯηϧΛ఻͑Δ 19

Slide 20

Slide 20 text

Copyright © 2023 treastrain / Tanaka RyogaɹAll rights reserved. for-await-inͷ͋ΔTaskʹΩϟϯηϧΛ఻͑Δ ͞·͟·ͳํ๏͕͋Δ w TaskΛ$PNCJOFͷ Cancellableʹద߹ͤ͞ɺ $PNCJOFͷPublisherͱ 
 ಉ͡Α͏ʹ 
 store(in: &cancellables) w ΦϦδφϧͰCancelBagΛ࡞Δ 
 ʢDisposeBagͷΑ͏ͳʣ w TaskΛอ͓͖࣋ͯ͠ɺ 
 deinitͳͲͷλΠϛϯάͰ Task.cancel()ΛࣗΒݺͿ w ෳ਺͋Δ৔߹͸TaskGroupͰ ·ͱΊͯ΋Α͍ 
 
 
 ɹɹɹɹɹɹɹɹɹFUD 20

Slide 21

Slide 21 text

Copyright © 2023 treastrain / Tanaka RyogaɹAll rights reserved. TaskΛ$PNCJOFͷCancellableʹ͢Δ ݁ہ$PNCJOF͕ཁΔ͕͓खܰ 21 extension ViewController { func connect() { Task { for await output in publisher.values { await presenter.handleEventA() } } Task { for try await output in observable.values { await presenter.handleEventB() } } } } import Combine import Foundation extension Task: Cancellable {} import Combine import UIKit final class ViewController: UIViewController { private let publisher: some Publisher = ... private var cancellables: [AnyCancellable] = [] // ... func connect() { Task { [publisher] in for await output in publisher.values { // ... } } .store(in: &cancellables) } // … }

Slide 22

Slide 22 text

Copyright © 2023 treastrain / Tanaka RyogaɹAll rights reserved. deinitͳͲͷλΠϛϯάͰTask.cancel()ΛݺͿ deinit͕ݺ͹ΕΔΑ͏ʹ஫ҙ͢ΔʢselfΛΩϟϓνϟ͠ͳ͍ʣ 22 extension ViewController { func connect() { Task { for await output in publisher.values { await presenter.handleEventA() } } Task { for try await output in observable.values { await presenter.handleEventB() } } } } final class ViewController: UIViewController { var task1: Task<(), Never>? var task2: Task<(), any Error>? deinit { task1?.cancel() task2?.cancel() } ... } extension ViewController { func connect() { task1 = Task { [publisher, presenter] in for await output in publisher.values { await presenter.handleEventA() } } task2 = Task { [publisher, presenter] in ... } } }

Slide 23

Slide 23 text

Copyright © 2023 treastrain / Tanaka RyogaɹAll rights reserved. ·ͱΊ $PNCJOFɾ3Y4XJGUˠ4XJGU$PODVSSFODZ w $PNCJOFɾ3Y4XJGUͷΠϕϯτΛ 
 4XJGU$PODVSSFODZʢAsyncSequenceʣͰ 
 ड͚औΕΔ࢓૊Έ͕༻ҙ͞Ε͍ͯΔ w Ҡߦظʹศརʹ࢖͑ͦ͏ w ΩϟϯηϧΛࣗ෼ͰߦΘͳ͍ͱղ์͞Εͣʹ࢒ͬͯ͠·͏ͷͰ஫ҙ͢Δ 23

Slide 24

Slide 24 text

Copyright © 2023 treastrain / Tanaka RyogaɹAll rights reserved. ͋Γ͕ͱ͏͍͟͝·ͨ͠ʂ $PNCJOFɾsinkʢ3Y4XJGUɾsubscribeʣͰ͸ 
 TaskΛ࡞Βͳ͍Α͏ʹͯ͠ 
 asyncͳϝιουΛݺͿ 4XJGU8FEOFTEBZ 24 ެ։4XJGU8FEOFTEBZʲJ04%$+BQBO௚લʳ"VHVTU