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)

    View Slide

  2. ͨͳͨͭ / Tatsuya Tanaka
    • Ϡϑʔגࣜձࣾ / iOSΞϓϦࠇଳ


    • TechFeed Expert
    @tattn
    @tanakasan2525
    @tattn

    View Slide

  3. VTuberελΠϧͰWebձٞʹࢀՃͰ͖ΔΞϓϦެ։த

    View Slide

  4. WWDC Extended Tokyo 2022։࠵
    IUUQTZKNFFUVQDPOOQBTTDPNFWFOU

    View Slide

  5. IUUQTJODSFNFOUTDPOOQBTTDPNFWFOU

    View Slide

  6. Swift 6Ͱඇಉظॲཧ͸

    ͞Βʹ γϯϓϧ Ͱ ؆୯

    ʹͳΔ

    View Slide

  7. ඇಉظॲཧͷ೉͍͠ͱ͜Ζ

    View Slide

  8. Մಡੑͷҡ࣋
    ഉଞ੍ޚ

    View Slide

  9. Մಡੑͷҡ࣋
    ഉଞ੍ޚ

    View Slide

  10. 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ʹ൓ө
    ߋ৽͕͋Ε͹ΞϥʔτΛग़͢

    View Slide

  11. ϦΞΫςΟϒϑϨʔϜϫʔΫͷՄಡੑ
    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ʹ൓ө
    ϚΠάϨʔγϣϯ݁ՌΛอଘ
    ߋ৽͕͋Ε͹ΞϥʔτΛग़͢
    ɾΦϖϨʔλ౳ͷཧղ͕ඞཁ


    ɾσόοάͷ೉қ౓্͕͕Δ


    ɾετϦʔϜ͕௕͘ͳΔͱՄಡੑ͕େ͖͘མͪΔ
    (ϦΞΫςΟϒͰ͸ͳ͍γϯϓϧͳඇಉظॲཧʹ͸ෳࡶ)

    View Slide

  12. Swift Concurrency
    Swift 5ܥ

    View Slide

  13. 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ʹ൓ө
    ϚΠάϨʔγϣϯ݁ՌΛอଘ
    ߋ৽͕͋Ε͹ΞϥʔτΛग़͢

    View Slide

  14. Մಡੑͷҡ࣋
    ഉଞ੍ޚ

    View Slide

  15. ഉଞ੍ޚ΁ͷߴ͍ҙࣝ
    εϨουηʔϑͰ͸ͳ͍ૢ࡞Λ͢Δ࣌͸


    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


    }


    }


    }

    View Slide

  16. Swift Concurrency
    Swift 5ܥ

    View Slide

  17. 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Λ࢖͏ͱσʔλڝ߹ՕॴΛϏϧυΤϥʔʹͰ͖Δ

    View Slide

  18. 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ίϯύΠϥʹνΣοΫΛ೚ͤΔ͜ͱͰ

    ࣮૷࣌ʹߟ͑Δ͜ͱ΍ɺίʔυϨϏϡʔͷෛ୲΋ݮΒͤΔ

    View Slide

  19. Swift 5ܥͷSwift Concurrency
    ɾॲཧϑϩʔ͕෼͔Γ΍͍͢


    ɾഉଞ੍ޚ͕ඞཁͳΒActorΛ࢖͑͹ྑ͍
    ؆୯
    γϯϓϧ
    ɾഉଞ੍ޚ͕ඞཁͳ෦෼ΛίϯύΠϥ͕ڭ͑ͯ͘ΕΔ

    View Slide

  20. Swift Concurrency
    Swift 6

    View Slide

  21. func run() {


    let ref = ReferenceValue()




    Task {


    ref.value += 1


    }


    }


    final class ReferenceValue {


    var value = 1


    }
    σʔλڝ߹νΣοΫͷݫີԽ
    Swift 5ܥͰ͸ແܯࠂɺSwift 6Ͱ͸Τϥʔ
    ❌ কདྷతʹσʔλڝ߹͕ى͜ΔՄೳੑ

    View Slide

  22. 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Ͱઃఆ͢Δ৔߹

    View Slide

  23. Swift 5Ͱ΋ܯࠂʹͰ͖Δ
    func run() {


    let ref = ReferenceValue()




    Task {


    ref.value += 1


    }


    }


    final class ReferenceValue {


    var value = 1


    }

    View Slide

  24. UIKitͷMainActorνΣοΫ΋༗ޮʹͳΔ
    func updateUI() {


    let bounds = UIScreen.main.bounds


    // ϨΠΞ΢τܭࢉ


    }
    @MainActor(unsafe) ͷνΣοΫ΋༗ޮʹͳΔͨΊ


    UIKitΛ࢖͍ͬͯΔ৔߹͸େม͔΋…


    কདྷతʹ͸@preconcurrencyͳͲͰஈ֊Ҡߦ͠΍͘͢ͳΔʁ

    View Slide

  25. Swift 6Ͱى͜ΔมԽ (Swift Concurrency)
    • ୭Ͱ΋ڧ੍తʹ҆શͳඇಉظίʔυʹಋ͔ΕΔ


    • ίʔυϨϏϡʔ΍σόοάͷίετΛݮΒͤΔ


    • αʔυύʔςΟ΍ΞϓϦ಺ͷίʔυͷasync/awaitԽ͕ਐΉ
    Swift 6Ͱඇಉظॲཧ͸

    ͞Βʹ γϯϓϧ Ͱ ؆୯

    ʹͳΔ

    View Slide

  26. ࢀߟࢿྉ
    ɾ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

    View Slide