Slide 1

Slide 1 text

4XJGU$PODVSSFODZΛར༻ ͨ͠6*7JFX$POUSPMMFSදࣔ ͷഉଞ੍ޚͷ࣮૷ J045FDI5BMLʲΈͯͶY.JSSBUJWʳ !UPJLJ

Slide 2

Slide 2 text

ࣗݾ঺հ !UPJLJ UPJLJ 5PTIJLJ5BLF[BXB w .JSSBUJW *OD w α΢φʔ🧖

Slide 3

Slide 3 text

໨࣍ w BTZODBXBJUͱ͸ w ը໘දࣔͷ࣮૷Ͱར༻͢Δ w ฒߦॲཧͷΩϟϯηϧ

Slide 4

Slide 4 text

4XJGU$PODVSSFODZ w ݴޠߏจͱͯ͠ͷBTZODBXBJU w ֤ฒߦॲཧؒͷؔ܎ੑΛ੔ཧ͢ΔͨΊͷ֓೦Ͱ͋Δ 4USVDUVSFE$PODVSSFODZ w ҆શͳฒߦॲཧΛهड़͢ΔͨΊͷ৽ͨͳܕͰ͋Δ"DUPS

Slide 5

Slide 5 text

BTZODBXBJU ඇಉظॲཧɺฒߦॲཧΛಉظॲཧͱಉ͡Α͏ͳॻ͖ํͰɺ ؆͔ܿͭਖ਼֬ʹॻ͚ΔΑ͏ʹͨ͠ߏจ

Slide 6

Slide 6 text

BTZODBXBJU BTZODͰඇಉظͷ݁ՌΛ໭Γ஋ͱͯ͠ఆٛ BXBJUͰதஅɾ࠶։͢Δ func fetchImage(url: URL) async throws -> UIImage? { let request = imageRequest(url: url) let (data, _) = try await URLSession.shared.data(for: request) guard let image = UIImage(data: data) else { return nil } return image }

Slide 7

Slide 7 text

ը໘දࣔͷ࣮૷Ͱར༻͢Δ

Slide 8

Slide 8 text

ͭͷτϦΨʔͰෳ਺ͷը໘ΛϞʔμϧදࣔ͢Δ৔߹ lBMSFBEZQSFTFOUJOHXBSOJOHz͕ൃੜ ޙଓͷը໘දࣔ͸࣮ߦ͞Εͳ͍ func tapButton() { let coverVC1 = CoverViewController(rootView: .init(item: .red)) present(coverVC1, animated: true) let coverVC2 = CoverViewController(rootView: .init(item: .yellow)) present(coverVC2, animated: true) }

Slide 9

Slide 9 text

BTZODBXBJUରԠ දࣔதͷ଴ػΛࣔ͢ܕͷఆٛ ฒߦॲཧͷ࣮૷ ฒߦॲཧͷݺͼग़͠

Slide 10

Slide 10 text

BTZODBXBJUରԠ දࣔதͷ଴ػΛࣔ͢ܕͷఆٛ ฒߦॲཧͷ࣮૷ ฒߦॲཧͷݺͼग़͠

Slide 11

Slide 11 text

දࣔதͷ଴ػΛࣔ͢ܕͷఆٛ TVTQFOE͢Δը໘Λࣔ͢QSPUPDPM @MainActor protocol SuspendableViewControllerProtocol: AnyObject { var didDisappearHandler: (() -> Void)? { get set } } typealias SuspendableViewController = UIViewController & SuspendableViewControllerProtocol

Slide 12

Slide 12 text

දࣔதͷ଴ػΛࣔ͢ܕͷఆٛ TVTQFOE͢Δը໘ͷ࣮૷ class CoverViewController: UIHostingController, SuspendableViewControllerProtocol { var didDisappearHandler: (() -> Void)? override func viewDidDisappear(_ animated: Bool) { super.viewDidDisappear(animated) didDisappearHandler?() } }

Slide 13

Slide 13 text

BTZODBXBJUରԠ දࣔதͷ଴ػΛࣔ͢ܕΛ༻ҙ ฒߦॲཧͷ࣮૷ ฒߦॲཧͷݺͼग़͠

Slide 14

Slide 14 text

XJUI$IFDLFE$POUJOVBUJPO ը໘දࣔ TVTQFOE dը໘ඇදࣔ SFTVNF ͷؒͷz଴ػzΛ ܧଓ DPOUJOVBUJPO Λར༻͠ఆٛ͢Δ extension UIViewController { func presentAsync(_ viewController: SuspendableViewController, animated: Bool, completion: (() -> Void)? = nil) async { present(viewController, animated: animated, completion: completion) await withCheckedContinuation { continuation in viewController.didDisappearHandler = { continuation.resume() } } } }

Slide 15

Slide 15 text

BTZODBXBJUରԠ දࣔதͷ଴ػΛࣔ͢ܕΛ༻ҙ ฒߦॲཧͷ࣮૷ ฒߦॲཧͷݺͼग़͠

Slide 16

Slide 16 text

5BTL TZODίʔυ͔ΒBTZODίʔυΛݺͼग़͢ func tapButton() { Task { let coverVC1 = CoverViewController(rootView: .init(item: .red)) await presentAsync(coverVC1, animated: true) let coverVC2 = CoverViewController(rootView: .init(item: .yellow)) await presentAsync(coverVC2, animated: true) } }

Slide 17

Slide 17 text

ฒߦॲཧͷΩϟϯηϧ

Slide 18

Slide 18 text

ը໘දࣔΛΩϟϯηϧ͍ͨ͠৔߹ ޙଓͷը໘දࣔΛΩϟϯηϧ͢Δ FYάϩʔόϧͳΤϥʔ͕ൃੜ࣌ʹը໘Λด͍ͨ͡

Slide 19

Slide 19 text

5BTLDBODFM 5BTLΠϯελϯεΛอ࣋͠ɺΩϟϯηϧΛݺͼग़͢ var task: Task? func tapButton() { DispatchQueue.main.asyncAfter(deadline: .now() + 3) { [weak self] in self?.task?.cancel() } self.task = Task { let coverVC1 = CoverViewController(rootView: .init(item: .red)) await presentAsync(coverVC1, animated: true) let coverVC2 = CoverViewController(rootView: .init(item: .yellow)) await presentAsync(coverVC2, animated: true) } }

Slide 20

Slide 20 text

ڠௐతͳΩϟϯηϧ 5BTLDBODFM ͸ɺ λεΫʹΩϟϯηϧ͞Εͨͱ͍͏ϑϥάΛཱͯΔ͚ͩͰɺ Ωϟϯηϧ͞Ε͔ͨͲ͏͔͸֤λεΫଆͰ֬ೝ͢Δඞཁ͕͋Δ IUUQTEPDTTXJGUPSHTXJGUCPPL-BOHVBHF(VJEF$PODVSSFODZIUNM

Slide 21

Slide 21 text

ߏ଄Խ͞Ε͍ͯͳ͍ฒߦੑ w 5BTLܕΛ௚઀ѻ͏Α͏ͳ৔߹ w ߏ଄Խ͞Ε͍ͯͳ͍ฒߦੑͰ͸ɺฒߦॲཧͷϥΠϑαΠΫϧ ΍ΩϟϯηϧΛ࣮૷ऀ͕؅ཧ͢Δ IUUQTEFWFMPQFSBQQMFDPNWJEFPTQMBZXXED

Slide 22

Slide 22 text

ΩϟϯηϧͷϋϯυϦϯά w 5BTLJT$BODFMMFE w 5BTLDIFDL$BODFMMBUJPO w XJUI5BTL$BODFMMBUJPO)BOEMFS

Slide 23

Slide 23 text

5BTLJT$BODFMMFE ݱࡏͷλεΫ͕Ωϟϯηϧ͞Ε͍ͯͨͱ͖ɺ೚ҙͷॲཧΛߦ͏ func presentAsync(_ viewController: SuspendableViewController, animated: Bool, completion: (() -> Void)? = nil) async { if Task.isCancelled { return } present(viewController, animated: animated, completion: completion) await withCheckedContinuation { continuation in viewController.didDisappearHandler = { continuation.resume() } } }

Slide 24

Slide 24 text

5BTLJT$BODFMMFE ݺͼग़͠ଆ func tapButton() { DispatchQueue.main.asyncAfter(deadline: .now() + 3) { [weak self] in self?.task?.cancel() } self.task = Task { let coverVC1 = CoverViewController(rootView: .init(item: .red)) await presentAsync(coverVC1, animated: true) let coverVC2 = CoverViewController(rootView: .init(item: .yellow)) await presentAsync(coverVC2, animated: true) } }

Slide 25

Slide 25 text

5BTLJT$BODFMMFE ޙଓͷը໘දࣔ͸࣮ߦ͞Εͳ͍

Slide 26

Slide 26 text

5BTLDIFDL$BODFMMBUJPO ݱࡏͷλεΫ͕Ωϟϯηϧ͞Ε͍ͯͨͱ͖ɺ $BODFMMBUJPO&SSPSͷྫ֎Λฦ͢ func presentAsync(_ viewController: SuspendableViewController, animated: Bool, completion: (() -> Void)? = nil) async throws { try Task.checkCancellation() present(viewController, animated: animated, completion: completion) await withCheckedContinuation { continuation in viewController.didDisappearHandler = { continuation.resume() } } }

Slide 27

Slide 27 text

5BTLDIFDL$BODFMMBUJPO ݺͼग़͠ଆͰͷΩϟϯηϧ࣌ͷϋϯυϦϯά func tapButton() { DispatchQueue.main.asyncAfter(deadline: .now() + 3) { [weak self] in self?.task?.cancel() } self.task = Task { do { let coverVC1 = CoverViewController(rootView: .init(item: .red)) try await presentAsync(coverVC1, animated: true) let coverVC2 = CoverViewController(rootView: .init(item: .yellow)) try await presentAsync(coverVC2, animated: true) } catch { // CancellationError dismiss(animated: true) } } }

Slide 28

Slide 28 text

5BTLDIFDL$BODFMMBUJPO BTZODؔ਺ͷ࣮ߦλΠϛϯάͰ ྫ֎Λݕ஌ɺը໘Λด͡Δ

Slide 29

Slide 29 text

Ωϟϯηϧ࣌ʹBTZODؔ਺ଆͰଈ࣌ʹߦ͏ϋϯυϦϯά func presentAsync(_ viewController: SuspendableViewController, …) async { if Task.isCancelled { return } present(viewController, animated: animated, completion: completion) await withTaskCancellationHandler( handler: { // Sendable Task { @MainActor in viewController.dismiss(animated: true) } }, operation: { await withCheckedContinuation { continuation in viewController.didDisappearHandler = { continuation.resume() } } } ) } XJUI5BTL$BODFMMBUJPO)BOEMFS

Slide 30

Slide 30 text

XJUI5BTL$BODFMMBUJPO)BOEMFS දࣔதͷը໘Λด͡Δ

Slide 31

Slide 31 text

·ͱΊ w DPOUJOVBUJPOΛར༻͠଴ػঢ়ଶΛBTZODBXBJUͰදݱͨ͠ w ಉظίʔυ͔ΒBTZODؔ਺Λݺͼग़͢ࡍʹ͸5BTLΛར༻͢Δ w ඇߏ଄Խ͞ΕͨฒߦੑͷΩϟϯηϧॲཧ͸खಈͰߦ͏ w 5BTLJT$BODFMMFE w 5BTLDIFDL$BODFMMBUJPO w XJUI5BTL$BODFMMBUJPO)BOEMFS

Slide 32

Slide 32 text

ࢀߟࢿྉ w 4XJGUͷฒߦॲཧʹ͍ͭͯ w IUUQTEFWFMPQFSBQQMFDPNKQOFXT JEPFVPU[ w $PODVSSFODZŠ5IF4XJGU1SPHSBNNJOH-BOHVBHF 4XJGU w IUUQTEPDTTXJGUPSHTXJGUCPPL-BOHVBHF(VJEF$PODVSSFODZIUNM w $PODVSSFODZc"QQMF%FWFMPQFS%PDVNFOUBUJPO w IUUQTEFWFMPQFSBQQMFDPNEPDVNFOUBUJPOTXJGUTXJGU@TUBOEBSE@MJCSBSZ DPODVSSFODZ

Slide 33

Slide 33 text

5IBOLT🙌