見出し画像

【じっくりSw1ftUI42】実践編12〜第24章 Swift構造化同時実行の概要②〜Taskの理解以降

さてと、前回

で、

同時実行処理とは何ぞや?
👉前提知識〜エラーハンドリングも含めた非同期処理

をやったので、今回は以降をやってく〜〜〜🕺

オイラの学び直しの話なんて必要ない人は、

で公開されてるみたいだからそっち見ればいいんじゃない?👀💦
さてとでは今回も早速、

じっくり第24章の続きを読んでく👓

Taskとは?

ここは冒頭のリンク先の解説を引用すっけど

  1. 非同期で実行される作業はすべて、Swift Taskクラスのインスタンス内で実行。

  2. アプリは複数のタスクを同時に実行でき、これらのタスクを階層的に構造化。

  3. 起動すると、 actSome()関数の非同期バージョンがTaskインスタンス内で実行。

  4. actLong()関数が呼び出されると、システムは関数コードが実行されるサブタスクを作成。

  5. タスク階層ツリーの観点から見ると、このサブタスクはactSome()親タスクの子に当たる。

  6. サブタスク内からの非同期関数への呼び出しはすべて、そのタスクの子になる。

  7. このタスク階層は、構造化された同時実行性が構築される基礎を形成。

  8. (例)子タスクは親から優先度などの属性を継承し、階層により、すべての子孫タスクが完了するまで親タスクが終了しないことが保証される。

  9. タスクをグループ化して、複数の非同期タスクを動的に起動できる。

要は、

親タスク、子タスクって
階層構造で動的に管理できる

てことみたいだね🧐

タスクのスケジュール

  • .high/.userInitiated:高

  • .medium:中

  • .low/.utility:低

  • .background:バックグラウンド

ってなってるみたいだね👀
(👉トランザクション処理みたいなもんかな、、、💦)

と、ここからコード

enum Essentials24ErrorHandringEnum: Error {
    case longTime
    case shortTime
}

struct Essentials24_2ContentsView: View {
    var body: some View {
        VStack{
            Button(action: {
                Task{
                    await main()
                }
            }){
                Text("非同期の状態確認処理")
            }
        }
    }
    
    func main() async {
        //実行
        let runTask = Task(priority: .high){
            await actSome()
        }
        //キャンセル確認
        if(!runTask.isCancelled){
            runTask.cancel()
        }
    }
    
    func actSome() async{
        Task{
            print("開始")
            print("開始時刻\(Date())")
            print("----------------------")
            async let result = actLong()
            print("終了時刻\(await result))")
            print("終了")
        }
    }
    
    func actLong() async -> Date{
        sleep(10)
        return Date()
    }
    
    func timeSpan(delay: UInt32) async throws{
        print("タップされました\(Date())")
        if delay < 1 {
            throw Essentials24ErrorHandringEnum.shortTime
        } else if delay > 1 {
            throw Essentials24ErrorHandringEnum.longTime
        }
        sleep(delay)
        print("タップ完了\(Date())")
    }
}

#Preview {
    Essentials24_2ContentsView()
}

てな感じでやると、

通常はこうなんだけど
キャンセルかどうかを確認して再実行みたいなこともできるってことが言いたいみたいだね👀
出来てるのか🧐?

切り離されたタスクの処理

        func detachedTask() async{
        Task.detached{
            await actSome()
        }
    }

    func detachedTaskStatus() async{
        let detachedTask = Task.detached(priority: .high){
            await actSome()
        }
        if(!detachedTask.isCancelled){
            detachedTask.cancel()
        }
    }

てな感じでやれば、切り離されたタスクについても、状態の確認とかスケジュールまで含めて対応できるってことが言いたいみたいだね👀💦
ちょっとタスクが多くなってきたので〜〜〜

struct Essentials24_2ContentsView: View {
    var body: some View {
        VStack{
            Button(action: {
                Task{
                    await main()
                }
            }){
                Text("非同期の状態確認処理")
            }
            .padding()
            Button(action: {
                Task{
                    await detachedTask()
                }
            }){
                Text("detachedタスク処理")
            }
            .padding()
            Button(action: {
                Task{
                    await detachedTaskStatus()
                }
            }){
                Text("detachedタスク状態確認処理")
            }
            .padding()
        }
    }
    
    func main(){
        //実行
        let runTask = Task(priority: .high){
            await actSome()
        }
        //キャンセル確認
        if(!runTask.isCancelled){
            runTask.cancel()
        }
    }
    
    func detachedTask(){
        Task.detached{
            await actSome()
        }
    }

    func detachedTaskStatus(){
        let detachedTask = Task.detached(priority: .high){
            await actSome()
        }
        if(!detachedTask.isCancelled){
            detachedTask.cancel()
        }
    }
    
    func actSome() async{
        Task{
            print("開始")
            print("開始時刻\(Date())")
            print("----------------------")
            async let result = actLong()
            print("終了時刻\(await result))")
            print("終了")
        }
    }
    
    func actLong() async -> Date{
        sleep(3)
        return Date()
    }
    
    func timeSpan(delay: UInt32) async throws{
        print("タップされました\(Date())")
        if delay < 1 {
            throw Essentials24ErrorHandringEnum.shortTime
        } else if delay > 1 {
            throw Essentials24ErrorHandringEnum.longTime
        }
        sleep(delay)
        print("タップ完了\(Date())")
    }
}

#Preview {
    Essentials24_2ContentsView()
}

てな感じにして〜〜〜

それぞれのボタンを増やして実行してみると、、、
確かに実行は出来てるみたいだね👀💦

タスクの優先度を指定

    func getTaskPriority() async{
        Task{
            let priority = Task.currentPriority
            print("\(priority)")
        }
    }

みたいな関数をひとつ追加して〜〜〜

            Button(action: {
                Task{
                    await getTaskPriority()
                }
            }){
                Text("タスクの優先度確認")
            }
            .padding()

みたいなボタンを追加して〜〜〜

一番下のボタンをタップすると〜〜〜
てな感じで取得でけた〜〜〜

ただし、

読み取り専用で取得するだけだから、変更までは出来ないみたいだね、、、
このメソッドでは
👀💦

キャンセル状態を取得して、

ボタンをまた追加して〜〜〜
    func checkCanceled() async{
        Task{
            await actSome()
        }
        if Task.isCancelled {
            print("状態はキャンセルです")
        } else {
            print("状態はキャンセル以外です")
        }
    }

みたいな感じでやると〜〜〜

てな感じにもできるし〜〜〜

他にも、

    func tryCancelTask() async{
        do {
            try Task.checkCancellation()
            print("キャンセルできました")
        } catch {
            print("キャンセルできませんでした")
        }
    }

てな感じで、

キャンセル試行のボタンを追加してタップすると〜〜〜
って感じdo try catchの中でも確認できた〜〜〜🕺

Task自体をキャンセルしたい時は(まずはここから教えるべきじゃね👀💦)

    func taskCancel() async {
        Task.cancel()
    }

で簡単にできます感を出してんだけど、

みたいな感じでエラーになるので〜〜〜(大嘘やん藁🤣)

これまでに出てきていたメソッドを参考に

    func taskCancel() async {
        let task = Task(){
            
        }
        task.cancel()
        if (task.isCancelled){
            print("キャンセルされました")
        } else {
            print("キャンセルされまていません")
        }
    }

で実行すると

てな感じでキャンセルされているのがわかる。

これに〜〜

    func taskCancel() async {
        let task = Task(){
            await actSome()
        }
        task.cancel()
        if (task.isCancelled){
            print("キャンセルされました")
        } else {
            print("キャンセルされまていません")
        }
    }

てな感じで改修して実行すると〜〜〜

てな感じで、キャンセル後にawaitが実行されてんね👀
    func taskCancel() async {
        let task = Task(){
            await actSome()
        }
        task.cancel()
        print("キャンセル:\(task.isCancelled)")
        if (task.isCancelled){
            print("キャンセルされました")
        } else {
            print("キャンセルされまていません")
            task.cancel()
        }
    }

にしてみて〜〜〜

みたいな感じでできなくもないんだけど、、、

いずれにしても

一回はTaskが実行された後に、確実にキャンセルしてるだけだね👀💦

タスクを安全に一時停止させたい時は

    func taskYield() async {
        await Task.yield()
    }

で行けるらしいけど、ここではサンプルが短すぎて用途がよくわからない。

タスクグループ

    func actSomeGroup() async{
        await withTaskGroup(of: Void.self){group in for runCnt in 1...10 {
            group.addTask {
                let result = await actLong()
                print("タスク完了:\(runCnt) = \(result)")
            }
        }
        }
    }

で実行してみると、、、

てな感じ

ここは、色々解説書いてるけど、

タスクが複数ある場合にまとめて同時に処理する

感じかな👀💦

ここでポイント①タスクグループで同時に処理した結果を見ると、

  1. すべてのタスクには、同時に実行されたことを示す同じ完了タイムスタンプが表示される

  2. タスクが起動された順序どおりに完了していない

  3. 同時実行性を使用する場合は、タスクが作成された順序で完了するという保証はない

たしかに、タイムスタンプはついてるけども順番もバラバラだし〜〜〜

ここでポイント②addTask()関数に加えて、タスクグループ内からアクセスできる他のいくつかのメソッドとプロパティ

  1. cancelAll() – グループ内のすべてのタスクをキャンセルするメソッド呼び出し

  2. isCancelled – タスク グループがすでにキャンセルされているかどうかを示すブール型プロパティ

  3. isEmpty – タスク グループ内にタスクが残っているかどうかを示すブール型プロパティ

てな感じで記載があるけど、ここについては記述も薄くサンプルコードもないので以下のリンク記事を参照してもらった方がいいかもね👀💦

データのバッティング回避

と、ここもエラーコードを最初に説明していて、却って混乱するので、完成形で〜〜〜

    func actSomeGroupAvoidDataRace() async{
        var dicTimeStamp: [Int: Date] = [:]
        await withTaskGroup(of: (Int, Date).self) { group in
            for runCnt in 1...10 {
                group.addTask {
                    return(runCnt, await actLong())
                }
            }
            for await (runTask, runDate) in group {
                dicTimeStamp[runTask] = runDate
            }
        }
        for (runTask, runDate) in dicTimeStamp {
                print("実行処理 = \(runTask), 処理日時 = \(runDate)")
        }
    }

実行すると

てな感じになるね👀

最後に非同期プロパティは、

構造体とかクラスでも実現可能ってゆーてんね👀

構造体でプロパティを設定

struct Essentials24_2AsyncStruct {
    var runDate: Date {
        get async {
            return await self.getTime()
        }
    }
    func getTime() async -> Date{
        sleep(1)
        return Date()
    }
}

コイツ用の関数を追加〜〜〜

    func actSomeFromStruct() async{
        let runStruct = Essentials24_2AsyncStruct()
        Task{
            let runDate = await runStruct.runDate
            print("実行日:\(runDate)")
        }
    }

実行すると〜〜〜

てな感じで実現できたね👀🕺

てか構造体で外に出した方が扱いやすそう🌱

とりあえず、本文の内容は以上🕺

この章のコードまとめ

enum Essentials24ErrorHandringEnum: Error {
    case longTime
    case shortTime
}

struct Essentials24_1ContentsView: View {
    var body: some View {
        Button(action: {
            Task{
                do{
                    try await timeSpan(delay: 1)
                } catch Essentials24ErrorHandringEnum.shortTime {
                    print("時間短すぎ")
                } catch Essentials24ErrorHandringEnum.longTime {
                    print("時間かかり過ぎ")
                } catch {
                    print("意味不明なエラー")
                }
                await actSome()
            }
        }){
            Text("果物屋さんの同期処理")
        }
    }
    
    func actSome() async{
        Task{
            print("開始")
            print("開始時刻\(Date())")
            print("----------------------")
            async let result = actLong()
            print("終了時刻\(await result))")
            print("終了")
        }
    }
    
    func actLong() async -> Date{
        sleep(3)
        return Date()
    }
    
    func timeSpan(delay: UInt32) async throws{
        print("タップされました\(Date())")
        if delay < 1 {
            throw Essentials24ErrorHandringEnum.shortTime
        } else if delay > 1 {
            throw Essentials24ErrorHandringEnum.longTime
        }
        sleep(delay)
        print("タップ完了\(Date())")
    }
}

#Preview {
    Essentials24_1ContentsView()
}

struct Essentials24_2AsyncStruct {
    var runDate: Date {
        get async {
            return await self.getTime()
        }
    }
    func getTime() async -> Date{
        sleep(1)
        return Date()
    }
}

struct Essentials24_2ContentsView: View {
    var body: some View {
        VStack{
            Button(action: {
                Task{
                    await main()
                }
            }){
                Text("非同期の状態確認処理")
            }
            .padding()
            Button(action: {
                Task{
                    await detachedTask()
                }
            }){
                Text("detachedタスク処理")
            }
            .padding()
            Button(action: {
                Task{
                    await detachedTaskStatus()
                }
            }){
                Text("detachedタスク状態確認処理")
            }
            .padding()
            Button(action: {
                Task{
                    await getTaskPriority()
                }
            }){
                Text("タスクの優先度確認")
            }
            .padding()
            Button(action: {
                Task{
                    await checkCanceled()
                }
            }){
                Text("キャンセル確認")
            }
            .padding()
            Button(action: {
                Task {
                    await tryCancelTask()
                }
            }){
                Text("キャンセル試行")
            }
            .padding()
            Button(action: {
                Task{
                    await taskCancel()
                }
            }){
                Text("タスクをキャンセル後、確認")
            }
            .padding()
            Button(action: {
                Task{
                    await taskYield()
                }
            }){
                Text("タスクを一時停止")
            }
            .padding()
            Button(action: {
                Task{
                    await actSomeGroup()
                }
            }){
                Text("タスクグループ")
            }
            .padding()
            Button(action: {
                Task{
                    await actSomeGroupAvoidDataRace()
                }
            }){
                Text("データバッティング回避")
            }
            .padding()
            Button(action: {
                Task{
                    await actSomeFromStruct()
                }
            }){
                Text("構造体から非同期プロパティ")
            }
            .padding()
        }
    }
    
    func main() async {
        //実行
        let runTask = Task(priority: .high){
            await actSome()
        }
        //キャンセル確認
        if(!runTask.isCancelled){
            runTask.cancel()
        }
    }
    
    func detachedTask() async{
        Task.detached{
            await actSome()
        }
    }
    
    func detachedTaskStatus() async{
        let detachedTask = Task.detached(priority: .high){
            await actSome()
        }
        if(!detachedTask.isCancelled){
            detachedTask.cancel()
        }
    }
    
    func getTaskPriority() async{
        Task{
            let priority = Task.currentPriority
            print("\(priority)")
        }
    }
    
    func checkCanceled() async{
        Task{
            await actSome()
        }
        if Task.isCancelled {
            print("状態はキャンセルです")
        } else {
            print("状態はキャンセル以外です")
        }
    }
    
    func tryCancelTask() async{
        do {
            try Task.checkCancellation()
            print("キャンセルできました")
        } catch {
            print("キャンセルできませんでした")
        }
    }
    
    func taskCancel() async{
        let task = Task(){
            await actSome()
        }
        task.cancel()
        print("キャンセル:\(task.isCancelled)")
        if (task.isCancelled){
            print("キャンセルされました")
        } else {
            print("キャンセルされまていません")
            task.cancel()
        }
    }
    
    func taskYield() async {
        await Task.yield()
    }
    
    func actSome() async{
        Task{
            print("開始")
            print("開始時刻\(Date())")
            print("----------------------")
            async let result = actLong()
            print("終了時刻\(await result))")
            print("終了")
        }
    }
    
    func actSomeGroup() async{
        await withTaskGroup(of: Void.self){group in for runCnt in 1...10 {
            group.addTask {
                let result = await actLong()
                print("タスク完了:\(runCnt) = \(result)")
            }
        }
        }
    }
    
    func actSomeGroupAvoidDataRace() async{
        var dicTimeStamp: [Int: Date] = [:]
        await withTaskGroup(of: (Int, Date).self) { group in
            for runCnt in 1...10 {
                group.addTask {
                    return(runCnt, await actLong())
                }
            }
            for await (runTask, runDate) in group {
                dicTimeStamp[runTask] = runDate
            }
        }
        for (runTask, runDate) in dicTimeStamp {
                print("実行処理 = \(runTask), 処理日時 = \(runDate)")
        }
    }
    
    func actSomeFromStruct() async{
        let runStruct = Essentials24_2AsyncStruct()
        Task{
            let runDate = await runStruct.runDate
            print("実行日:\(runDate)")
        }
    }
    
    func actLong() async -> Date{
        sleep(3)
        return Date()
    }
    
    func timeSpan(delay: UInt32) async throws{
        print("タップされました\(Date())")
        if delay < 1 {
            throw Essentials24ErrorHandringEnum.shortTime
        } else if delay > 1 {
            throw Essentials24ErrorHandringEnum.longTime
        }
        sleep(delay)
        print("タップ完了\(Date())")
    }
}

#Preview {
    Essentials24_2ContentsView()
}

Apple公式

その他の参考リンク

今回の感想

本自体が、この章にasync関係の内容を中途半端に詰め込み過ぎって感じが凄いね👀💦
メソッドやプロパティの例も薄いし、実際にスレッド処理なんかで動く処理を見せながらじゃないと、

ふ〜〜〜んこんな処理があるんだ

くらいで普通は素通りされそう。。。。しかも3つくらいサンプルコードが正常に動かないものがこの章に潜んでいたし、、、

.cancel()

については、説明なしでいきなりぶっ込んできて、Task .cancel()って思いっきり誤ったコードを例示してるのはびっくりした。

インスタンスに直接メソッドぶち込んじゃダメだろ🧐

  • 変な先入観とか凝った構成にならないように

  • いきなり自分で動かしながら率直に感じたことを

  • 記事に認めるスタンス

でこのマガジンはやってるから、初見でも賄えるコードは、都度改修しながら動かして結果をいつも提示するようにしてるけど、

今回のは流石にちょっときちいなくらい内容も多いし、コードも誤ってる箇所が致命的だったので、難しかった😨

あくまでも、ここの章については、

こんな処理の方法があるんだ

くらいで、下手に一回で全部を理解しようとかしなくて大丈夫だと思う🧐
下手に完璧を求めるとここは却って混乱するかな💦

さてと、次回は

同時実行の続きみたいなもんな

第25章 Swift アクターの紹介

に入ってく〜〜〜〜🕺

記事公開後、

いつもどおり、

でやった操作を〜〜〜


てな
てな
てなの
てな感じ

サンプルコード

import SwiftUI
import WebKit

//タイトル
let essentialsChapter24NavigationTitle = "第24章"
let essentialsChapter24Title = "第24章 Swift 構造化同時実行の概要"
let essentialsChapter24_1SubTitle = "第1節 基本(Task、async、await)〜エラーハンドリングまで"
let essentialsChapter24_2SubTitle = "第2節 Taskの理解以降"

//コード
let codeEssentials24_1 = """
struct Essentials24_1ContentsView: View {
    var body: some View {
        Button(action: {
            Task{
                do{
                    try await timeSpan(delay: 1)
                } catch Essentials24ErrorHandringEnum.shortTime {
                    print("時間短すぎ")
                } catch Essentials24ErrorHandringEnum.longTime {
                    print("時間かかり過ぎ")
                } catch {
                    print("意味不明なエラー")
                }
                await actSome()
            }
        }){
            Text("果物屋さんの同期処理")
        }
    }

    func actSome() async{
        Task{
            print("開始")
            print("開始時刻\(Date())")
            print("----------------------")
            async let result = actLong()
            print("終了時刻\\(await result))")
            print("終了")
        }
    }
        
    func actLong() async -> Date{
        sleep(3)
        return Date()
    }
    
    func timeSpan(delay: UInt32) async throws{
        print("タップされました\(Date())")
        if delay < 1 {
            throw Essentials24ErrorHandringEnum.shortTime
        } else if delay > 1 {
            throw Essentials24ErrorHandringEnum.longTime
        }
        sleep(delay)
        print("タップ完了\(Date())")
    }
}
"""
let codeEssentials24_2 = """
struct Essentials24_2AsyncStruct {
    var runDate: Date {
        get async {
            return await self.getTime()
        }
    }
    func getTime() async -> Date{
        sleep(1)
        return Date()
    }
}

struct Essentials24_2ContentsView: View {
    var body: some View {
        VStack{
            Button(action: {
                Task{
                    await main()
                }
            }){
                Text("非同期の状態確認処理")
            }
            .padding()
            Button(action: {
                Task{
                    await detachedTask()
                }
            }){
                Text("detachedタスク処理")
            }
            .padding()
            Button(action: {
                Task{
                    await detachedTaskStatus()
                }
            }){
                Text("detachedタスク状態確認処理")
            }
            .padding()
            Button(action: {
                Task{
                    await getTaskPriority()
                }
            }){
                Text("タスクの優先度確認")
            }
            .padding()
            Button(action: {
                Task{
                    await checkCanceled()
                }
            }){
                Text("キャンセル確認")
            }
            .padding()
            Button(action: {
                Task {
                    await tryCancelTask()
                }
            }){
                Text("キャンセル試行")
            }
            .padding()
            Button(action: {
                Task{
                    await taskCancel()
                }
            }){
                Text("タスクをキャンセル後、確認")
            }
            .padding()
            Button(action: {
                Task{
                    await taskYield()
                }
            }){
                Text("タスクを一時停止")
            }
            .padding()
            Button(action: {
                Task{
                    await actSomeGroup()
                }
            }){
                Text("タスクグループ")
            }
            .padding()
            Button(action: {
                Task{
                    await actSomeGroupAvoidDataRace()
                }
            }){
                Text("データバッティング回避")
            }
            .padding()
            Button(action: {
                Task{
                    await actSomeFromStruct()
                }
            }){
                Text("構造体から非同期プロパティ")
            }
            .padding()
        }
    }
    
    func main() async {
        //実行
        let runTask = Task(priority: .high){
            await actSome()
        }
        //キャンセル確認
        if(!runTask.isCancelled){
            runTask.cancel()
        }
    }
    
    func detachedTask() async{
        Task.detached{
            await actSome()
        }
    }
    
    func detachedTaskStatus() async{
        let detachedTask = Task.detached(priority: .high){
            await actSome()
        }
        if(!detachedTask.isCancelled){
            detachedTask.cancel()
        }
    }
    
    func getTaskPriority() async{
        Task{
            let priority = Task.currentPriority
            print("\\(priority)")
        }
    }
    
    func checkCanceled() async{
        Task{
            await actSome()
        }
        if Task.isCancelled {
            print("状態はキャンセルです")
        } else {
            print("状態はキャンセル以外です")
        }
    }
    
    func tryCancelTask() async{
        do {
            try Task.checkCancellation()
            print("キャンセルできました")
        } catch {
            print("キャンセルできませんでした")
        }
    }
    
    func taskCancel() async{
        let task = Task(){
            await actSome()
        }
        task.cancel()
        print("キャンセル:\\(task.isCancelled)")
        if (task.isCancelled){
            print("キャンセルされました")
        } else {
            print("キャンセルされまていません")
            task.cancel()
        }
    }
    
    func taskYield() async {
        await Task.yield()
    }
    
    func actSome() async{
        Task{
            print("開始")
            print("開始時刻\(Date())")
            print("----------------------")
            async let result = actLong()
            print("終了時刻\\(await result))")
            print("終了")
        }
    }
    
    func actSomeGroup() async{
        await withTaskGroup(of: Void.self){group in for runCnt in 1...10 {
            group.addTask {
                let result = await actLong()
                print("タスク完了:\\(runCnt) = \\(result)")
            }
        }
        }
    }
    
    func actSomeGroupAvoidDataRace() async{
        var dicTimeStamp: [Int: Date] = [:]
        await withTaskGroup(of: (Int, Date).self) { group in
            for runCnt in 1...10 {
                group.addTask {
                    return(runCnt, await actLong())
                }
            }
            for await (runTask, runDate) in group {
                dicTimeStamp[runTask] = runDate
            }
        }
        for (runTask, runDate) in dicTimeStamp {
                print("実行処理 = \\(runTask), 処理日時 = \\(runDate)")
        }
    }
    
    func actSomeFromStruct() async{
        let runStruct = Essentials24_2AsyncStruct()
        Task{
            let runDate = await runStruct.runDate
            print("実行日:\\(runDate)")
        }
    }
    
    func actLong() async -> Date{
        sleep(3)
        return Date()
    }
    
    func timeSpan(delay: UInt32) async throws{
        print("タップされました\(Date())")
        if delay < 1 {
            throw Essentials24ErrorHandringEnum.shortTime
        } else if delay > 1 {
            throw Essentials24ErrorHandringEnum.longTime
        }
        sleep(delay)
        print("タップ完了\(Date())")
    }
}

#Preview {
    Essentials24_2ContentsView()
}
"""

//ポイント
let pointEssentials24_1 = """
◾️ここでポイント①
 ここまでのコードを見ていてお気づきの人もいるとは思うんだが、、、この章のこれまでのコードも実は、教科書どおりに打って見ても実はエラーになる箇所が2箇所あるので、動くようにサンプルコードを変えてます🤣
 ここも多分、async入れる箇所と、await入れる箇所を検証もせずに頭でコードだけ売ってるからこんなことになる藁🤣

 ①必ずサンプルコードだけを読んで理解した気にならずに、
 ②早い段階で必ずサンプルコードが正常に動くかを実際に動かしてみて検証し、
 ③動かないなら、なんで動かないかを調べよう🕺
 👉練習とか自己学習の段階でならいくらでも失敗していいんだけど、理論とかコードの作りからだけで理解した気になって、いざリリースした後で発覚してスレッド処理のコードを全部書き換えとか恐ろしくて仕方なくなるからね🧐お客さんに余計な費用かけるし、リリースできなくなると商機を逸するし〜〜〜

◾️ここでポイント②
 エラーハンドリングは実際の現場では、
 ビューのボタンなんかがちゃんと反応したかなんかを返すときに、こーゆー処理は入れる

◾️ここでポイント③
 do-catch文については、忘れていた人も多いと思うので、リンク先の記事で見返してね〜〜〜🕺
 この記事で書いていた時は、Playground=あくまでもSwiftってプログラミング言語のSyntax:文法でやってただけで、SwiftUIフレームワークでの実践では初めてやるのに、最初はエラーが出まくって当たり前だからね〜〜〜
 SwiftUIフレームワークで動かしながら、SwiftUI用のコードの書き方を学ぶ👉実践編なので、、、
 一回で出来ない、一回しか習ってないのに忘れる=当たり前(てか別に最終的に使いこなせればいいだけ👉一回で覚える必要なんてない=お受験勉強ではないからね)
 覚える🔛忘れる、コードを書く🔛エラーが出るを繰り返して、ゆくゆくは
 いつの間にか何も意識せずに自分の作りたいアプリが作れる=至高のフロー
 状態になればいいだけだから。
 気にしない気にしない。
 エラーが出まくっても、なんでエラーが出たのかを最初のうちは理解すればいいだけ。
 逆に典型的な理系出身者とかで記憶するのが当たり前、全部知っていて当たり前みたいな人も過去の現場でゆーてる人を見たことあるけど、丸暗記したところで、自分のアプリでは一生使わないか進化し続けるプログラミング言語でまた別のフレームワークが主流になれば、どうせ変更されるんだから=効率が悪すぎるし、そんな人ほど、変化に対応できなくなって、COBOLの時代なんかの感覚で完璧に理解しようとか覚えようとして失敗してるからね〜〜〜
 成れの果ては、他の記事なんかでも書いてるとおり🕺
 ま、今や客先常駐の派遣技術者に頼りまくって、難易度とかを理解してないまま、予算の関係なんかで簡単に使い捨ててきた結果、個人で趣味でやってる人よりも受け入れ先の企業の方が最新の言語に関する知識や技能=本来の情報資産がない人材しか揃っていないなんて逆転現象が起きてるからね〜〜〜〜🧐
 (それでいいのか?ってのはあるけど、アプリを作りたいってだけなオイラとかみんなには関係ない話。それはその企業が今後どーするかだけだからね。)
"""
let pointEssentials24_2 = """
本自体が、この章にasync関係の内容を中途半端に詰め込み過ぎって感じが凄いね👀💦
メソッドやプロパティの例も薄いし、実際にスレッド処理なんかで動く処理を見せながらじゃないと、

ふ〜〜〜んこんな処理があるんだ

くらいで普通は素通りされそう。。。。しかも3つくらいサンプルコードが正常に動かないものがこの章に潜んでいたし、、、
.cancel()
については、説明なしでいきなりぶっ込んできて、Task .cancel()って思いっきり誤ったコードを例示してるのはびっくりした。
インスタンスに直接メソッドぶち込んじゃダメだろ🧐
変な先入観とか凝った構成にならないように
いきなり自分で動かしながら率直に感じたことを
記事に認めるスタンス
でこのマガジンはやってるから、初見でも賄えるコードは、都度改修しながら動かして結果をいつも提示するようにしてるけど、
今回のは流石にちょっときちいなくらい内容も多いし、コードも誤ってる箇所が致命的だったので、難しかった😨
あくまでも、ここの章については、
こんな処理の方法があるんだ
くらいで、下手に一回で全部を理解しようとかしなくて大丈夫だと思う🧐
下手に完璧を求めるとここは却って混乱するかな💦
"""
//URL
let urlEssentials24_1 = "https://note.com/m_kakudo/n/n864d914b9b54"
let urlEssentials24_2 = "https://note.com/m_kakudo/n/nad1cfff0ae9b"

//ビュー管理構造体
struct ListiOSApp17DevelopmentEssentialsCh24: Identifiable {
    var id: Int
    var title: String
    var view: ViewEnumiOSApp17DevelopmentEssentialsCh24
}
//遷移先の画面を格納する列挙型
enum ViewEnumiOSApp17DevelopmentEssentialsCh24{
    case Sec1
    case Sec2
}
//各項目に表示するリスト項目
let dataiOSApp17DevelopmentEssentialsCh24: [ListiOSApp17DevelopmentEssentialsCh24] = [
    ListiOSApp17DevelopmentEssentialsCh24(id: 1, title: essentialsChapter24_1SubTitle, view: .Sec1),
    ListiOSApp17DevelopmentEssentialsCh24(id: 2, title: essentialsChapter24_2SubTitle, view: .Sec2),
]
struct iOSApp17DevelopmentEssentialsCh24: View {
    var body: some View {
        VStack {
            Divider()
            List (dataiOSApp17DevelopmentEssentialsCh24) { data in
                self.containedViewiOSApp17DevelopmentEssentialsCh24(dataiOSApp17DevelopmentEssentialsCh24: data)
            }
            .edgesIgnoringSafeArea([.bottom])
        }
        .navigationTitle(essentialsChapter24NavigationTitle)
        .navigationBarTitleDisplayMode(.inline)
    }
    //タップ後に遷移先へ遷移させる関数
    func containedViewiOSApp17DevelopmentEssentialsCh24(dataiOSApp17DevelopmentEssentialsCh24: ListiOSApp17DevelopmentEssentialsCh24) -> AnyView {
        switch dataiOSApp17DevelopmentEssentialsCh24.view {
        case .Sec1:
            return AnyView(NavigationLink (destination: Essentials24_1()) {
                Text(dataiOSApp17DevelopmentEssentialsCh24.title)
            })
        case .Sec2:
            return AnyView(NavigationLink (destination: Essentials24_2()) {
                Text(dataiOSApp17DevelopmentEssentialsCh24.title)
            })
        }
    }
}
#Preview {
    iOSApp17DevelopmentEssentialsCh24()
}

struct Essentials24_1: View {
    var body: some View {
        VStack{
            TabView {
                Essentials24_1ContentsView()
                    .tabItem {
                        Image(systemName: contentsImageTab)
                        Text(contentsTextTab)
                    }
                Essentials24_1Code()
                    .tabItem {
                        Image(systemName: codeImageTab)
                        Text(codeTextTab)
                    }
                Essentials24_1Points()
                    .tabItem {
                        Image(systemName: pointImageTab)
                        Text(pointTextTab)
                    }
                Essentials24_1WEB()
                    .tabItem {
                        Image(systemName: webImageTab)
                        Text(webTextTab)
                    }
            }
        }
    }
}
#Preview {
    Essentials24_1()
}

struct Essentials24_1Code: View {
    var body: some View {
        ScrollView{
            Text(codeEssentials24_1)
        }
    }
}
#Preview {
    Essentials24_1Code()
}
struct Essentials24_1Points: View {
    var body: some View {
        ScrollView{
            Text(pointEssentials24_1)
        }
    }
}
#Preview {
    Essentials24_1Points()
}
struct Essentials24_1WebView: UIViewRepresentable {
    let searchURL: URL
    func makeUIView(context: Context) -> WKWebView {
        let view = WKWebView()
        let request = URLRequest(url: searchURL)
        view.load(request)
        return view
    }
    func updateUIView(_ uiView: WKWebView, context: Context) {
        
    }
}
struct Essentials24_1WEB: View {
    private var url:URL = URL(string: urlEssentials24_1)!
    var body: some View {Essentials24_1WebView(searchURL: url)
    }
}
#Preview {
    Essentials24_1WEB()
}

struct Essentials24_2: View {
    var body: some View {
        VStack{
            TabView {
                Essentials24_2ContentsView()
                    .tabItem {
                        Image(systemName: contentsImageTab)
                        Text(contentsTextTab)
                    }
                Essentials24_2Code()
                    .tabItem {
                        Image(systemName: codeImageTab)
                        Text(codeTextTab)
                    }
                Essentials24_2Points()
                    .tabItem {
                        Image(systemName: pointImageTab)
                        Text(pointTextTab)
                    }
                Essentials24_2WEB()
                    .tabItem {
                        Image(systemName: webImageTab)
                        Text(webTextTab)
                    }
            }
        }
    }
}
#Preview {
    Essentials24_2()
}

struct Essentials24_2Code: View {
    var body: some View {
        ScrollView{
            Text(codeEssentials24_2)
        }
    }
}
#Preview {
    Essentials24_2Code()
}
struct Essentials24_2Points: View {
    var body: some View {
        ScrollView{
            Text(pointEssentials24_2)
        }
    }
}
#Preview {
    Essentials24_2Points()
}
struct Essentials24_2WebView: UIViewRepresentable {
    let searchURL: URL
    func makeUIView(context: Context) -> WKWebView {
        let view = WKWebView()
        let request = URLRequest(url: searchURL)
        view.load(request)
        return view
    }
    func updateUIView(_ uiView: WKWebView, context: Context) {
        
    }
}
struct Essentials24_2WEB: View {
    private var url:URL = URL(string: urlEssentials24_2)!
    var body: some View {Essentials24_2WebView(searchURL: url)
    }
}
#Preview {
    Essentials24_2WEB()
}

enum Essentials24ErrorHandringEnum: Error {
    case longTime
    case shortTime
}

struct Essentials24_1ContentsView: View {
    var body: some View {
        Button(action: {
            Task{
                do{
                    try await timeSpan(delay: 1)
                } catch Essentials24ErrorHandringEnum.shortTime {
                    print("時間短すぎ")
                } catch Essentials24ErrorHandringEnum.longTime {
                    print("時間かかり過ぎ")
                } catch {
                    print("意味不明なエラー")
                }
                await actSome()
            }
        }){
            Text("果物屋さんの同期処理")
        }
    }
    
    func actSome() async{
        Task{
            print("開始")
            print("開始時刻\(Date())")
            print("----------------------")
            async let result = actLong()
            print("終了時刻\(await result))")
            print("終了")
        }
    }
    
    func actLong() async -> Date{
        sleep(3)
        return Date()
    }
    
    func timeSpan(delay: UInt32) async throws{
        print("タップされました\(Date())")
        if delay < 1 {
            throw Essentials24ErrorHandringEnum.shortTime
        } else if delay > 1 {
            throw Essentials24ErrorHandringEnum.longTime
        }
        sleep(delay)
        print("タップ完了\(Date())")
    }
}

#Preview {
    Essentials24_1ContentsView()
}

struct Essentials24_2AsyncStruct {
    var runDate: Date {
        get async {
            return await self.getTime()
        }
    }
    func getTime() async -> Date{
        sleep(1)
        return Date()
    }
}

struct Essentials24_2ContentsView: View {
    var body: some View {
        VStack{
            Button(action: {
                Task{
                    await main()
                }
            }){
                Text("非同期の状態確認処理")
            }
            .padding()
            Button(action: {
                Task{
                    await detachedTask()
                }
            }){
                Text("detachedタスク処理")
            }
            .padding()
            Button(action: {
                Task{
                    await detachedTaskStatus()
                }
            }){
                Text("detachedタスク状態確認処理")
            }
            .padding()
            Button(action: {
                Task{
                    await getTaskPriority()
                }
            }){
                Text("タスクの優先度確認")
            }
            .padding()
            Button(action: {
                Task{
                    await checkCanceled()
                }
            }){
                Text("キャンセル確認")
            }
            .padding()
            Button(action: {
                Task {
                    await tryCancelTask()
                }
            }){
                Text("キャンセル試行")
            }
            .padding()
            Button(action: {
                Task{
                    await taskCancel()
                }
            }){
                Text("タスクをキャンセル後、確認")
            }
            .padding()
            Button(action: {
                Task{
                    await taskYield()
                }
            }){
                Text("タスクを一時停止")
            }
            .padding()
            Button(action: {
                Task{
                    await actSomeGroup()
                }
            }){
                Text("タスクグループ")
            }
            .padding()
            Button(action: {
                Task{
                    await actSomeGroupAvoidDataRace()
                }
            }){
                Text("データバッティング回避")
            }
            .padding()
            Button(action: {
                Task{
                    await actSomeFromStruct()
                }
            }){
                Text("構造体から非同期プロパティ")
            }
            .padding()
        }
    }
    
    func main() async {
        //実行
        let runTask = Task(priority: .high){
            await actSome()
        }
        //キャンセル確認
        if(!runTask.isCancelled){
            runTask.cancel()
        }
    }
    
    func detachedTask() async{
        Task.detached{
            await actSome()
        }
    }
    
    func detachedTaskStatus() async{
        let detachedTask = Task.detached(priority: .high){
            await actSome()
        }
        if(!detachedTask.isCancelled){
            detachedTask.cancel()
        }
    }
    
    func getTaskPriority() async{
        Task{
            let priority = Task.currentPriority
            print("\(priority)")
        }
    }
    
    func checkCanceled() async{
        Task{
            await actSome()
        }
        if Task.isCancelled {
            print("状態はキャンセルです")
        } else {
            print("状態はキャンセル以外です")
        }
    }
    
    func tryCancelTask() async{
        do {
            try Task.checkCancellation()
            print("キャンセルできました")
        } catch {
            print("キャンセルできませんでした")
        }
    }
    
    func taskCancel() async{
        let task = Task(){
            await actSome()
        }
        task.cancel()
        print("キャンセル:\(task.isCancelled)")
        if (task.isCancelled){
            print("キャンセルされました")
        } else {
            print("キャンセルされまていません")
            task.cancel()
        }
    }
    
    func taskYield() async {
        await Task.yield()
    }
    
    func actSome() async{
        Task{
            print("開始")
            print("開始時刻\(Date())")
            print("----------------------")
            async let result = actLong()
            print("終了時刻\(await result))")
            print("終了")
        }
    }
    
    func actSomeGroup() async{
        await withTaskGroup(of: Void.self){group in for runCnt in 1...10 {
            group.addTask {
                let result = await actLong()
                print("タスク完了:\(runCnt) = \(result)")
            }
        }
        }
    }
    
    func actSomeGroupAvoidDataRace() async{
        var dicTimeStamp: [Int: Date] = [:]
        await withTaskGroup(of: (Int, Date).self) { group in
            for runCnt in 1...10 {
                group.addTask {
                    return(runCnt, await actLong())
                }
            }
            for await (runTask, runDate) in group {
                dicTimeStamp[runTask] = runDate
            }
        }
        for (runTask, runDate) in dicTimeStamp {
                print("実行処理 = \(runTask), 処理日時 = \(runDate)")
        }
    }
    
    func actSomeFromStruct() async{
        let runStruct = Essentials24_2AsyncStruct()
        Task{
            let runDate = await runStruct.runDate
            print("実行日:\(runDate)")
        }
    }
    
    func actLong() async -> Date{
        sleep(3)
        return Date()
    }
    
    func timeSpan(delay: UInt32) async throws{
        print("タップされました\(Date())")
        if delay < 1 {
            throw Essentials24ErrorHandringEnum.shortTime
        } else if delay > 1 {
            throw Essentials24ErrorHandringEnum.longTime
        }
        sleep(delay)
        print("タップ完了\(Date())")
    }
}

#Preview {
    Essentials24_2ContentsView()
}

以上🕺

じゃまた来週

この記事が気に入ったらサポートをしてみませんか?