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

Swift Concurrencyによる安全で快適な非同期処理

Swift Concurrencyによる安全で快適な非同期処理

Swift6がもたらす開発者体験を予測しよう! #QiitaNight
https://increments.connpass.com/event/246181/

アバターアプリ:
https://tattn.fanbox.cc/posts/3541601

WWDC Extended Tokyo 2022:
https://yj-meetup.connpass.com/event/247614/

Tatsuya Tanaka

May 18, 2022
Tweet

More Decks by Tatsuya Tanaka

Other Decks in Programming

Transcript

  1. Swift ConcurrencyʹΑΔ 
 ҆શͰշదͳඇಉظॲཧ Tatsuya Tanaka / ాத ୡ໵ Qiita

    Night-Swift6͕΋ͨΒ͢։ൃऀମݧΛ༧ଌ͠Α͏ʂ (#QiitaNight)
  2. Completion HandlerͷՄಡੑ func configureApp() { let group = DispatchGroup() (0...2).forEach

    { _ in group.enter() } // アプリの更新チェック var updateInfo: UpdateInfo! forcedUpdate.checkUpdates { updateInfo = $0 group.leave() } // データマイグレーション dataMigrator.migration { // データマイグレーションの結果を保存 saveMigrationResult($0) group.leave() } var weather: Weather! // 現在地を取得 locationAPI.getLocation { // 現在地の天気を取得 weatherAPI.request(location) { weather = $0 group.leave() } } group.notify(queue: .main) { guard !updateInfo.isUpdateRequired else { // アプリのアップデートを促す } // UIにデータを反映 } } ࿈ଓͨ͠ඇಉظॲཧͷՄಡੑ͕௿͍ (ίʔυͷॲཧॱ্͕͔ΒԼʹྲྀΕ͍ͯͳ͍) ΞϓϦͷߋ৽νΣοΫ σʔλϚΠάϨʔγϣϯ ݱࡏ஍औಘ ݱࡏ஍ͷఱؾΛऔಘ ϚΠάϨʔγϣϯ݁ՌΛอଘ UIʹ൓ө ߋ৽͕͋Ε͹ΞϥʔτΛग़͢
  3. ϦΞΫςΟϒϑϨʔϜϫʔΫͷՄಡੑ func configureApp() { Publishers.Zip3( // アプリの更新チェック forcedUpdate.checkUpdates(), // データマイグレーション

    dataMigrator.migration().map(saveMigrationResult), // 現在地の天気を取得 locationAPI.getLocation().map(weatherAPI.request) ) .sink { (updateInfo, _, weather) in guard !updateInfo.isUpdateRequired else { // アプリのアップデートを促す } // UIにデータを反映 } .store(in: &cancellables) } ίʔυྔ͸ݮΔ͕ɺ ΞϓϦͷߋ৽νΣοΫ σʔλϚΠάϨʔγϣϯ ݱࡏ஍औಘ ݱࡏ஍ͷఱؾΛऔಘ UIʹ൓ө ϚΠάϨʔγϣϯ݁ՌΛอଘ ߋ৽͕͋Ε͹ΞϥʔτΛग़͢ ɾΦϖϨʔλ౳ͷཧղ͕ඞཁ ɾσόοάͷ೉қ౓্͕͕Δ ɾετϦʔϜ͕௕͘ͳΔͱՄಡੑ͕େ͖͘མͪΔ (ϦΞΫςΟϒͰ͸ͳ͍γϯϓϧͳඇಉظॲཧʹ͸ෳࡶ)
  4. async/await func configureApp() async throws { // アプリの更新チェック async let

    updateInfo = forcedUpdate.checkUpdates() // データマイグレーション async let migrationResult = dataMigrator.migration() // 現在地の天気を取得 async let weather = try weatherAPI.request( locationAPI.getLocation() ) // データマイグレーションの結果を保存 await saveMigrationResult(migrationResult) guard await !updateInfo.isUpdateRequired else { // アプリのアップデートを促す } // UIにデータを反映 } ಉظॲཧͷΑ͏ͳݟͨ໨ͷίʔυʹͳΓ 
 ϑϩʔΛ௥͍΍͍͢ ΞϓϦͷߋ৽νΣοΫ σʔλϚΠάϨʔγϣϯ ݱࡏ஍औಘ ݱࡏ஍ͷఱؾΛऔಘ UIʹ൓ө ϚΠάϨʔγϣϯ݁ՌΛอଘ ߋ৽͕͋Ε͹ΞϥʔτΛग़͢
  5. ഉଞ੍ޚ΁ͷߴ͍ҙࣝ εϨουηʔϑͰ͸ͳ͍ૢ࡞Λ͢Δ࣌͸ data race͕ൃੜ͠ͳ͍Α͏ʹҙ࣮ࣝͯ͠૷͢Δ let queue = DispatchQueue(label: "sample.code.qiitanight") var

    landmarksInRegion: [Region: [Landmark]] = [:] /// 現在地の区画が更新された時に呼ばれる /// - Parameter region: 現在地が含まれる区画 func onUpdateRegion(_ region: Region) { // 周辺のランドマーク検索 searchLandmarks(in: region) { [weak self] landmarks in queue.async { self?.landmarksInRegion[region] = landmarks } } }
  6. Actor @globalActor struct LocationActor { actor ActorType {} static let

    shared = ActorType() } @LocationActor var landmarksInRegion: [Region: [Landmark]] = [:] /// 現在地の区画が更新された時に呼ばれる /// - Parameter region: 現在地が含まれる区画 func onUpdateRegion(_ region: Region) async { // 周辺のランドマーク検索 let landmarks = await searchLandmarks(in: region) landmarksInRegion[region] = landmarks } ActorΛ࢖͏ͱσʔλڝ߹ՕॴΛϏϧυΤϥʔʹͰ͖Δ
  7. Actor @globalActor struct LocationActor { actor ActorType {} static let

    shared = ActorType() } @LocationActor var landmarksInRegion: [Region: [Landmark]] = [:] /// 現在地の区画が更新された時に呼ばれる /// - Parameter region: 現在地が含まれる区画 @LocationActor func onUpdateRegion(_ region: Region) async { // 周辺のランドマーク検索 let landmarks = await searchLandmarks(in: region) landmarksInRegion[region] = landmarks } SwiftίϯύΠϥʹνΣοΫΛ೚ͤΔ͜ͱͰ 
 ࣮૷࣌ʹߟ͑Δ͜ͱ΍ɺίʔυϨϏϡʔͷෛ୲΋ݮΒͤΔ
  8. func run() { let ref = ReferenceValue() Task { ref.value

    += 1 } } final class ReferenceValue { var value = 1 } σʔλڝ߹νΣοΫͷݫີԽ Swift 5ܥͰ͸ແܯࠂɺSwift 6Ͱ͸Τϥʔ ❌ কདྷతʹσʔλڝ߹͕ى͜ΔՄೳੑ
  9. Swift 6ͰϏϧυΤϥʔʹͳΔՕॴΛࣄલʹௐ΂Δ let package = Package( ... ) package.targets.forEach {

    $0.swiftSettings = [.unsafeFlags(["-Xfrontend", "-warn-concurrency", "-Xfrontend", "-enable-actor-data-race-checks"])] } OTHER_SWIFT_FLAGS = -Xfrontend -warn-concurrency -Xfrontend -enable-actor-data-race-checks Swift 5ܥͰ΋Swift 6ͰΤϥʔʹͳΔՕॴΛܯࠂʹͰ͖Δ SwiftPMͷ৔߹ XcodeͰઃఆ͢Δ৔߹
  10. Swift 5Ͱ΋ܯࠂʹͰ͖Δ func run() { let ref = ReferenceValue() Task

    { ref.value += 1 } } final class ReferenceValue { var value = 1 }
  11. UIKitͷMainActorνΣοΫ΋༗ޮʹͳΔ func updateUI() { let bounds = UIScreen.main.bounds // ϨΠΞ΢τܭࢉ

    } @MainActor(unsafe) ͷνΣοΫ΋༗ޮʹͳΔͨΊ UIKitΛ࢖͍ͬͯΔ৔߹͸େม͔΋… কདྷతʹ͸@preconcurrencyͳͲͰஈ֊Ҡߦ͠΍͘͢ͳΔʁ
  12. ࢀߟࢿྉ ɾOn the road to Swift 6 ɹɾhttps://forums.swift.org/t/on-the-road-to-swift-6/32862 ɾIncremental migration

    to concurrency checking 
 ɹɾhttps://github.com/apple/swift-evolution/blob/main/proposals/0337-support-incremental-migration-to-concurrency-checking.md ɾPiecemeal adoption of Swift 6 improvements in Swift 5.x 
 ɹɾhttps://forums.swift.org/t/piecemeal-adoption-of-swift-6-improvements-in-swift-5-x/57184 ɾSendable and @Sendable closures ɹɾhttps://github.com/apple/swift-evolution/blob/main/proposals/0302-concurrent-value-and-concurrent-closures.md