メインコンテンツへスキップ
見出し画像

【じっくりSw1ftUI 29】Essentials第15章〜Playgroundで遊ぼう15〜エラーハンドリング エラーの警告は、お友達。エラーとうまく付き合えばいいだけ。

    さてと、前回

    で

    配列と辞書(ディクショナリ)

    まで完了したので、

    💃Swiftに関する基本文法(シンタックス)はひととおり完了🕺
    記事に書いてる方としては、いやあ長かった💦

    今回がシンタックス編の最終章

    第15章 エラーハンドリング

    をやってく〜〜〜🕺

    今までやってきたことを簡単にまとめると、

    1. 引数(定数、変数)

    2. 演算子

    3. 関数とメソッド

    4. 制御

    5. クラス

    6. サブクラス=継承

    7. 構造体と列挙型

    8. プロトコル

    9. プロパティラッパー

    10. 配列と辞書(ディクショナリ)

    11. エラーハンドリング👈今回はココ!

    てな感じになるんだけど、実は、1〜9までが、

    文法の書き方:表

    で、今回が、

    書いた文法でエラーが起きた場合にどうするか:裏

    って感じの話になるってイメージかな。

    本題に入る前に

    前回の最後で、コードのデバッグ練習の方法については、ちょろっと書いたんだけど、例えば

    画像
    これは普通にOKなコードなんだけど、、、
    //変更前
    yaoya14_13.removeValue(forKey: 7)
    ↓
    //変更後
    yaoya14_13.removeValue(forKey 7)

    って感じで、

    yaoya14_13.removeValue(forKey: 7)

    のコロンを消してみるだけで、

    画像
    赤丸エラーが発生する
    赤丸をクリックすると、、、
    画像
    エラーの詳細が出てくるので〜〜〜
    Fixをクリックすると
    画像
    てな感じで、修正はしてくれるんだけど、
    これを実行すると実は、
    画像
    うまく動かない👀
    理由は簡単で、

    構文に書き方が合っていないから!

    なんだけど、これって

    エラーの警告は全て消えてるんだけど、
    実際に動かしてみないと、
    実際に動くかどうかがわからないんだよね〜〜〜

    なので構造体の章あたりから、結構口酸っぱく

    論理的に頭でコードを書くだけで終わるのではなく、
    コードを書いたら、必ず早めに動かすこと!

    って言ってきてたんだよね〜〜〜。

    ま、この修正は簡単で、

    画像
    メソッドの直後まで消して、(を入力
    👉予測候補が出てくるので、そいつをクリックして〜〜〜
    画像
    数字を入れて、実行するだけ
    画像
    ホイ、エラーが解消されて、元通りになりました🕺

    なんでこんなまどろっこしい説明をしてるかってゆーと、

    実践編で、実際にXcodeにSwiftUIフレームワークを使って結構色々な組み込み例をメインにやるけど、

    SwiftUI=コーディングベースで開発が主流
    (attribute inspectorでローコードみたいな開発もできなくはないが、
    慣れてくるとやらなくなる)

    ので、

    ・無意識に手が当たって、何かのコードが1文字消えた
    ↓
    ・バグ警告でバグfixできたので、表示上はバグが消えた:実際にはこれで8割ぐらいは解消できるんだけど、、、
    ↓
    ・実際に動かしてみたら、全然想定通りに動かないか、動的エラー発生

    みたいな感じで、

    頭だけで書いたコードで、警告も出てないからOKで一回も検証しないままリリースとかすると

    後から涙目😢

    になるからね。
    さてと、デバッグの方法についてはイメージがつかめたところで、

    エラーハンドリングの本題に入りませう

    今回もオイラの学びなんてどーでもいいって人は、

    に本の内容は丸々乗っているみたいなのでお好きにどうぞ。

    じっくり第15章を読んでく

    まず概要として

    完璧な世界ではまずエラーなんて起こることがない
    しかし、現実にはエラーが起きないことなんてない

    ってことを書いてんね👀
    (個人的には、そんな完璧な世界=理想を現実世界に投影して、他者に押し付けてきてんのは、自称プロな素人だけ:エラー拒否症候群)

    んでこの章では、

    • エラーの種類

    • エラー対処のメソッドと関数

    • guard文とdefer文の書き方

    なんかを紹介するって言ってんね。それでさらに、

    ここでポイント①:エラーハンドリングを理解する

    1. iOS アプリのメソッド内で望ましい結果が得られない場合にエラーをトリガー (またはスロー) する

    2. メソッドによってスローされたエラーをキャッチして処理する

    さらに、エラーがスローされた場合

    • そのエラーは特定のタイプのエラーであり、これを使用してエラーの特定の性質を識別し、取るべき最も適切なアクションを決定できる。

    • エラー タイプの値には、エラー プロトコルに準拠する任意の値を指定できる。

    なんのこっちゃ?🧐

    って感じなので、実際にまずは、

    エラー種類を宣言してみる

    オイラはこんな感じで書いてみた

    //エラーを宣言
    enum ErrorTypes15_1: Error{
        case warning
        case wrong
        case fatal
    }

    んでもって、エラーをスローする関数なんかを追加
    (この手法は、実は導入編のコードテンプレートでもやってるのでわかると思う👉いつものお決まりな感じのヤツ🕺)

    //エラー処理関数
    func errorHandling15_2() throws{
        
    }

    てな感じでまずは関数を用意して〜〜〜
    *エラーの戻り値の型を追加することも可

    //エラー処理関数:戻り値は真偽型
    func errorHandling15_2() throws -> Bool{
        
    }

    変数を追加

    //変数を宣言
    var warning15_3 = false
    var wrong15_3 = false
    var fatal15_3 = true
    var errorWarningMsg15_3 = "Warn"
    var errorWrongMsg15_3 = "Wrong"
    var errorFatalMsg15_3 = "Fatal"

    組み込み〜〜〜

    //エラー処理関数
    func errorHandling15_2() throws {
        guard warning15_3 == true else {
            throw ErrorTypes15_1.warning
        }
        guard wrong15_3 == true else {
            throw ErrorTypes15_1.wrong
        }
        guard fatal15_3 == true else {
            throw ErrorTypes15_1.fatal
        }
    }

    てな感じかな🧐

    このパターンは、

    で最後にやったguard文も使いながら、

    偽の場合に、エラータイプを返す
    👉真か偽で特に偽=falseの場合の書き方

    do-catch文

    スローされた処理をキャッチしてさらに処理をしたい場合には、do-catchで宣言して、確実にキャッチしないと処理結果が取れないので〜〜〜

    //3つとも場合分け
    func returnErrorHandling15_4() -> String{
        do {
            try errorHandling15_2()
        } catch ErrorTypes15_1.warning {
            return("Error is warning")
        } catch ErrorTypes15_1.wrong{
            return("Error is wrong")
        } catch ErrorTypes15_1.fatal {
            return("Error is fatal")
        } catch {
            return("Error is unknown")
        }
        return("Error is nothing")
    }

    てな感じで場合分けもできるし〜〜〜

    //fatalかどうかで場合分け
    func returnErrorHandling15_5() -> String{
        do {
            try errorHandling15_2()
        } catch ErrorTypes15_1.warning, ErrorTypes15_1.wrong {
            return("Error is not fatal")
        } catch ErrorTypes15_1.fatal {
            return("Error is fatal")
        } catch {
            return("Error is unknown")
        }
        return("Error is nothing")
    }

    みたいな感じで分類することも可能。

    👉ケースバイケースだけど、オイラなら、前者を使って確実に細かく分ける

    (理由)

    ①実行結果が、warningなのでwrongなのか分からない
    ②その後でさらに細かい処理に繋げることがほとんど

    なので〜〜〜。何かの処理の途中であくまでも、

    fatalかfatal以外かの判定だけをしたい場合には後者も有効

    だとは思うけどね。

    エラーの値を呼び出し(アクセス)

    サンプルコードだと、ディレクトリにアクセスして〜〜〜

    って感じで実践的にやってるんだけど、エラーハンドリングって記事の趣旨とちょっと毛色が違うのと、他の解説まで必要になってくるので、ここでは、単純に、

    //3つとも場合分け
    func returnErrorHandling15_6() -> String{
        do {
            try errorHandling15_2()
        } catch ErrorTypes15_1.warning {
            print("Error is warning")
            return("Error is warning")
        } catch ErrorTypes15_1.wrong{
            print("Error is wrong")
            return("Error is wrong")
        } catch ErrorTypes15_1.fatal {
            print("Error is fatal")
            return("Error is fatal")
        } catch {
            print("Error is unknown")
            return("Error is unknown")
        }
        print("Error is nothing")
        return("Error is nothing")
    }
    //実行
    returnErrorHandling15_6()

    てな感じで実行すると

    画像
    てな感じで、wraningの処理結果が返された

    ちなみに、

    画像
    てな感じで、returnをコメントアウトして実行すると、
    画像
    てな感じで、エラーになる

    なぜかといえば、簡単な話なんだけど、

    catchした後に返す=returnする値がないから!

    ってだけ。
    ま、単純な話なんだけど、慣れていないとなんでエラーになるのか分からないので気をつけよう

    あと、do-catch文に慣れていないと最初よくやるのが

    //3つとも場合分け
    func returnErrorHandling15_6() -> String{
        do {
            try errorHandling15_2()
        } catch ErrorTypes15_1.warning {
            return("Error is warning")
            print("Error is warning")
        } catch ErrorTypes15_1.wrong{
            return("Error is wrong")
            print("Error is wrong")
        } catch ErrorTypes15_1.fatal {
            return("Error is fatal")
            print("Error is fatal")
        } catch {
            return("Error is unknown")
            print("Error is unknown")
        }
        return("Error is nothing")
        print("Error is nothing")
    }
    //実行
    returnErrorHandling15_6()

    てな書き方で、さっきのと良く似てるんだけど、

    returnとprintの順番が逆でreturnが先に来てるパターン

    これを実行しても、

    画像
    ログに出力されてない

    理由はこちらも簡単で、

    returnの時点で処理が終わって値を返してるから
    後続のprint文は未実行で終わってるから

    ってだけ!

    他にも、

    //エラーコード
    var errorCode15_6 = 3

    みたいな引数を追加してあげて

    //3つとも場合分け
    func returnErrorHandling15_6() -> String{
        do {
            try errorHandling15_2()
        } catch ErrorTypes15_1.warning {
            errorCode15_6 += 100
            print("Error is warning,ErrorCode: \(errorCode15_6)")
            return("Error is warning")
        } catch ErrorTypes15_1.wrong{
            errorCode15_6 += 2000
            print("Error is wrong,ErrorCode: \(errorCode15_6)")
            return("Error is wrong")
        } catch ErrorTypes15_1.fatal {
            errorCode15_6 += 10000
            print("Error is fatal,ErrorCode: \(errorCode15_6)")
            return("Error is fatal")
        } catch {
            print("Error is unknown")
            return("Error is unknown")
        }
        print("Error is nothing")
        return("Error is nothing")
    }
    //実行
    returnErrorHandling15_6()

    てな感じで実行すると、

    画像
    みたいなこともできるし、
    //3つとも場合分け:エラーコードのみ
    func returnErrorHandling15_7() -> String{
        do {
            try errorHandling15_2()
        } catch ErrorTypes15_1.warning {
            errorCode15_6 = 3
            errorCode15_6 += 100
            print("ErrorCode: \(errorCode15_6)")
            return("Error is warning")
        } catch ErrorTypes15_1.wrong{
            errorCode15_6 = 3
            errorCode15_6 += 2000
            print("ErrorCode: \(errorCode15_6)")
            return("Error is wrong")
        } catch ErrorTypes15_1.fatal {
            errorCode15_6 = 3
            errorCode15_6 += 10000
            print("ErrorCode: \(errorCode15_6)")
            return("Error is fatal")
        } catch {
            print("Error is unknown")
            return("Error is unknown")
        }
        print("Error is nothing")
        return("Error is nothing")
    }
    //実行
    returnErrorHandling15_7()

    で、
    (*ひとつ前の関数でエラーコードに加算してしまっているので、
    一旦、エラーコードをここでは初期値に戻してあげて)

    画像
    みたいな感じで、エラーコードのみでどのエラーかを判別することもでけた👀🌱

    try! errorHandling15_2()で無効化もできなくはないみたいで、

    メソッド呼び出しによってエラーがスローされないことを確実に知っていることをコンパイラーに通知する場合に使うんだけど、

    さて問題です。

    「メソッド呼び出しによってエラーがスローされないことを確実に知っていることをコンパイラーに通知する場合に使う」
    てどういうことでしょう?

    メソッド呼び出しによってエラーがスローされないことを確実に知っている

    てことは、それってそもそもなんでじゃあエラー処理をする必要があるんだ?👀って話。だって、

    エラーが起きた時に確実にスローされないことを知ってるんだよ?
    👉実行するまでもなくわかってるんだったら、そもそもそのエラー自体を文章に書かないか、そのエラー自体を直した方が良くないか?🧐

    って感じで、すでに他の対処方法がパッと思いつくだけでも出てくるので、

    ここは、こんな感じで無効化できる方法があるんだ!くらいで頭の片隅に残しておけば今のところOKじゃないかな。

    要はエラーになるんだけど、
    処理を途中でスローさせたくない
    って時に使うくらいなイメージでいいと思う。

    defer文

    要は、どのdo-catch文でどの処理が返ってきても確実に行いたい処理を実行させる〜〜〜

    って感じで

    //3つとも場合分け
    func returnErrorHandling15_6() -> String{
        defer{
            //初期値の3に戻す
            errorCode15_6 = 3
        }
        do {
            try errorHandling15_2()
        } catch ErrorTypes15_1.warning {
            errorCode15_6 += 100
            print("Error is warning,ErrorCode: \(errorCode15_6)")
            return("Error is warning")
        } catch ErrorTypes15_1.wrong{
            errorCode15_6 += 2000
            print("Error is wrong,ErrorCode: \(errorCode15_6)")
            return("Error is wrong")
        } catch ErrorTypes15_1.fatal {
            errorCode15_6 += 10000
            print("Error is fatal,ErrorCode: \(errorCode15_6)")
            return("Error is fatal")
        } catch {
            print("Error is unknown")
            return("Error is unknown")
        }
        print("Error is nothing")
        return("Error is nothing")
    }
    //実行
    returnErrorHandling15_6()

    てな感じでしといてあげると、

    returnErrorHandling15_7()

    を実行した後でも、確実に

    画像
    てな感じで、初期値に戻って実行できるって感じね。

    ま、オイラなら作法として、

    //3つとも場合分け:エラーコードのみ
    func returnErrorHandling15_7() -> String{
        defer{
            errorCode15_6 = 3
        }
        do {
            try errorHandling15_2()
        } catch ErrorTypes15_1.warning {
            errorCode15_6 += 100
            print("ErrorCode: \(errorCode15_6)")
            return("Error is warning")
        } catch ErrorTypes15_1.wrong{
            errorCode15_6 += 2000
            print("ErrorCode: \(errorCode15_6)")
            return("Error is wrong")
        } catch ErrorTypes15_1.fatal {
            errorCode15_6 += 10000
            print("ErrorCode: \(errorCode15_6)")
            return("Error is fatal")
        } catch {
            print("Error is unknown")
            return("Error is unknown")
        }
        print("Error is nothing")
        return("Error is nothing")
    }
    //実行
    returnErrorHandling15_7()

    次の処理に入るまでに、初期値に戻した方がいいものは、どの関数の中でも確実に元に戻すようにしとくけどね👀💦

    じゃないと、

    func returnErrorHandling15_7() -> String{
        do {
            try errorHandling15_2()
        } catch ErrorTypes15_1.warning {
            errorCode15_6 += 100
            print("ErrorCode: \(errorCode15_6)")
            return("Error is warning")
        } catch ErrorTypes15_1.wrong{
            errorCode15_6 += 2000
            print("ErrorCode: \(errorCode15_6)")
            return("Error is wrong")
        } catch ErrorTypes15_1.fatal {
            errorCode15_6 += 10000
            print("ErrorCode: \(errorCode15_6)")
            return("Error is fatal")
        } catch {
            print("Error is unknown")
            return("Error is unknown")
        }
        print("Error is nothing")
        return("Error is nothing")
    }
    //実行
    returnErrorHandling15_7()
    for n in 1...100{
        returnErrorHandling15_7()
    }

    みたいなコードを実行した場合

    画像
    てな感じで想定してないエラーコードが出てくることになるけども、

    deferさえ入れておけば、繰り返し文の中でも

    画像
    確実に初期値に返ってから次の処理に入ってくれる
    👉エラー処理自体が想定外のものにならなくて済む

    と、ここの章のお話は以上🕺

    今回のまとめコード

    //変数を宣言
    var warning15_3 = false
    var wrong15_3 = false
    var fatal15_3 = true
    var errorWarningMsg15_3 = "Warn"
    var errorWrongMsg15_3 = "Wrong"
    var errorFatalMsg15_3 = "Fatal"
    //エラーコード
    var errorCode15_6 = 3
    
    //エラーを宣言
    enum ErrorTypes15_1: Error{
        case warning
        case wrong
        case fatal
    }
    //エラー処理関数
    func errorHandling15_2() throws {
        guard warning15_3 == true else {
            throw ErrorTypes15_1.warning
        }
        guard wrong15_3 == true else {
            throw ErrorTypes15_1.wrong
        }
        guard fatal15_3 == true else {
            throw ErrorTypes15_1.fatal
        }
    }
    //try-catch文
    //3つとも場合分け
    func returnErrorHandling15_4() -> String{
        do {
            try errorHandling15_2()
        } catch ErrorTypes15_1.warning {
            return("Error is warning")
        } catch ErrorTypes15_1.wrong{
            return("Error is wrong")
        } catch ErrorTypes15_1.fatal {
            return("Error is fatal")
        } catch {
            return("Error is unknown")
        }
        return("Error is nothing")
    }
    //fatalかどうかで場合分け
    func returnErrorHandling15_5() -> String{
        do {
            try errorHandling15_2()
        } catch ErrorTypes15_1.warning, ErrorTypes15_1.wrong {
            return("Error is not fatal")
        } catch ErrorTypes15_1.fatal {
            return("Error is fatal")
        } catch {
            return("Error is unknown")
        }
        return("Error is nothing")
    }
    //3つとも場合分け
    func returnErrorHandling15_6() -> String{
        defer{
            //初期値の3に戻す
            errorCode15_6 = 3
        }
        do {
            try errorHandling15_2()
        } catch ErrorTypes15_1.warning {
            errorCode15_6 += 100
            print("Error is warning,ErrorCode: \(errorCode15_6)")
            return("Error is warning")
        } catch ErrorTypes15_1.wrong{
            errorCode15_6 += 2000
            print("Error is wrong,ErrorCode: \(errorCode15_6)")
            return("Error is wrong")
        } catch ErrorTypes15_1.fatal {
            errorCode15_6 += 10000
            print("Error is fatal,ErrorCode: \(errorCode15_6)")
            return("Error is fatal")
        } catch {
            print("Error is unknown")
            return("Error is unknown")
        }
        print("Error is nothing")
        return("Error is nothing")
    }
    //実行
    returnErrorHandling15_6()
    //3つとも場合分け:エラーコードのみ
    func returnErrorHandling15_7() -> String{
        defer{
            errorCode15_6 = 3
        }
        do {
            try errorHandling15_2()
        } catch ErrorTypes15_1.warning {
            errorCode15_6 += 100
            print("ErrorCode: \(errorCode15_6)")
            return("Error is warning")
        } catch ErrorTypes15_1.wrong{
            errorCode15_6 += 2000
            print("ErrorCode: \(errorCode15_6)")
            return("Error is wrong")
        } catch ErrorTypes15_1.fatal {
            errorCode15_6 += 10000
            print("ErrorCode: \(errorCode15_6)")
            return("Error is fatal")
        } catch {
            print("Error is unknown")
            return("Error is unknown")
        }
        print("Error is nothing")
        return("Error is nothing")
    }
    //実行
    returnErrorHandling15_7()
    for n in 1...100{
        returnErrorHandling15_7()
    }

    なんでこの章通りに変数で宣言してるかは、これまでの記事で何回も書いてるから、ちゃんと自分で見返してみてね

    Apple公式

    さてと、次回から

    いよいよ実践編

    に入っていくけども、本の順番通り

    第16章 SwiftUIの概観

    に進んでいこうかな💦結構、導入編

    なんかでまとめた記事とオーバラップはしてそうだけど、気づきも多そうだしね。導入編の説明で漏れてるところで全く触ったことがない人には大事なことも多そうなんで。

    で予習したい人は予習しといてね〜〜〜あくまでもオイラは、

    自分の学び直しで記事を公開してるだけなので〜〜〜

    嫁がそろそろMOSのACCESSの勉強に入りそうだし、シンタックス編もひととおり終わったので、

    一旦、じっくりSw1ftUIシリーズの執筆はお休みして、

    で

    ACCESSの概観を何回かの記事に分けてお話ししようか考え中🤔
    ま、そのままSwiftをやるかもだけどね〜〜〜
    数週間くらいはお休みしたほうが、
    Swiftの基本文法の復習にちょうどいい人もいるかもしれないし。

    この前予告した通りだけど、

    で、これまでのシンタックスの記事はリンクまとめてるからそっちも覗いてもらえたら幸い🕺

    ではではまた次回
    みなさんも良い3連休を〜〜〜

    記事公開後

    今回も、

    でやった方法にいつも通りしたがって〜〜〜

    画像
    てな
    画像
    てな
    画像
    感じではい完了🕺

    コード

    ◆Essentials15.swift

    import SwiftUI
    import WebKit
    
    struct Essentials15: View {
        var body: some View {
            VStack{
                TabView {
                    Essentials15Code()
                        .tabItem {
                            Image(systemName: codeImageTab)
                            Text(codeTextTab)
                        }
                    Essentials15Points()
                        .tabItem {
                            Image(systemName: pointImageTab)
                            Text(pointTextTab)
                        }
                    Essentials15WEB()
                        .tabItem {
                            Image(systemName: webImageTab)
                            Text(webTextTab)
                        }
                }
            }
        }
    }
     #Preview  {
        Essentials15()
    }
    
    struct Essentials15Code: View {
        var body: some View {
            ScrollView{
                Text(codeEssentials15)
            }
        }
    }
     #Preview  {
        Essentials15Code()
    }
    
    struct Essentials15Points: View {
        var body: some View {
            ScrollView{
                Text(pointEssentials15)
            }
        }
    }
     #Preview  {
        Essentials15Points()
    }
    
    struct Essentials15WebView: 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 Essentials15WEB: View {
        private var url:URL = URL(string: urlEssentials15)!
        var body: some View {
            Essentials15WebView(searchURL: url)
        }
    }
     #Preview  {
        Essentials15WEB()
    }

    ◆iOSApp17DevelopmentEssentialsCh15.swift

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

    ◆iOSApp17DevelopmentEssentials.swift

    import SwiftUI
    
    //ビュー管理構造体
    struct ListiOSApp17DevelopmentEssentials: Identifiable {
        var id: Int
        var title: String
        var view: ViewEnumiOSApp17DevelopmentEssentials
    }
    
    //遷移先の画面を格納する列挙型
    enum ViewEnumiOSApp17DevelopmentEssentials {
        case Ch1
        //じっくり13で追加
        case Ch2
        //じっくり14で追加
        case Ch3
        //じっくり15で追加
        case Ch4
        //じっくり16で追加
        case Ch5
        //じっくり17で追加
        case Ch6
        //じっくり18で追加
        case Ch7
        //じっくり19で追加
        case Ch8
        //じっくり20、21で追加
        case Ch9
        //じっくり22、23で追加
        case Ch10
        //じっくり24で追加
        case Ch11
        //じっくり25で追加
        case Ch12
        //じっくり26で追加
        case Ch13
        //じっくり27,28で追加
        case Ch14
        //じっくり29で追加
        case Ch15
    }
    
    //各項目に表示する文字列
    let dataiOSApp17DevelopmentEssentials: [ListiOSApp17DevelopmentEssentials] = [
        ListiOSApp17DevelopmentEssentials(id: 1, title: essentialsChapter1Title, view: .Ch1),
        //じっくり13で追加
        ListiOSApp17DevelopmentEssentials(id: 2, title: essentialsChapter2Title, view: .Ch2),
        //じっくり13で追加
        ListiOSApp17DevelopmentEssentials(id: 3, title: essentialsChapter3Title, view: .Ch3),
        //じっくり15で追加
        ListiOSApp17DevelopmentEssentials(id: 4, title: essentialsChapter4Title, view: .Ch4),
        //じっくり16で追加
        ListiOSApp17DevelopmentEssentials(id: 5, title: essentialsChapter5Title, view: .Ch5),
        //じっくり17で追加
        ListiOSApp17DevelopmentEssentials(id: 6, title: essentialsChapter6Title, view: .Ch6),
        //じっくり18で追加
        ListiOSApp17DevelopmentEssentials(id: 7, title: essentialsChapter7Title, view: .Ch7),
        //じっくり19で追加
        ListiOSApp17DevelopmentEssentials(id: 8, title: essentialsChapter8Title, view: .Ch8),
        //じっくり20、21で追加
        ListiOSApp17DevelopmentEssentials(id: 9, title: essentialsChapter9Title, view: .Ch9),
        //じっくり22、23で追加
        ListiOSApp17DevelopmentEssentials(id: 10, title: essentialsChapter10Title, view: .Ch10),
        //じっくり24で追加
        ListiOSApp17DevelopmentEssentials(id: 11, title: essentialsChapter11Title, view: .Ch11),
        //じっくり25で追加
        ListiOSApp17DevelopmentEssentials(id: 12, title: essentialsChapter12Title, view: .Ch12),
        //じっくり26で追加
        ListiOSApp17DevelopmentEssentials(id: 13, title: essentialsChapter13Title, view: .Ch13),
        //じっくり27,28で追加
        ListiOSApp17DevelopmentEssentials(id: 14, title: essentialsChapter14Title, view: .Ch14),
        //じっくり29で追加
        ListiOSApp17DevelopmentEssentials(id: 15, title: essentialsChapter15Title, view: .Ch15)
    ]
    
    struct iOSApp17DevelopmentEssentials: View {
        var body: some View {
            VStack {
                Divider()
                List (dataiOSApp17DevelopmentEssentials) { data in
                    self.containedViewiOSApp17DevelopmentEssentials(dataiOSApp17DevelopmentEssentials: data)
                }
                .edgesIgnoringSafeArea([.bottom])
            }
            .navigationTitle("iOS開発の章目次")
            .navigationBarTitleDisplayMode(.inline)
        }
        //タップ後に遷移先へ遷移させる関数
        func containedViewiOSApp17DevelopmentEssentials(dataiOSApp17DevelopmentEssentials: ListiOSApp17DevelopmentEssentials) -> AnyView {
            switch dataiOSApp17DevelopmentEssentials.view {
            case .Ch1:
                return AnyView(NavigationLink (destination: iOSApp17DevelopmentEssentialsCh1()) {
                    Text(dataiOSApp17DevelopmentEssentials.title)
                })
                //じっくり13で追加
            case .Ch2:
                return AnyView(NavigationLink (destination: iOSApp17DevelopmentEssentialsCh2()) {
                    Text(dataiOSApp17DevelopmentEssentials.title)
                })
                //じっくり13で追加
            case .Ch3:
                return AnyView(NavigationLink (destination: iOSApp17DevelopmentEssentialsCh3()) {
                    Text(dataiOSApp17DevelopmentEssentials.title)
                })
                //じっくり15で追加
            case .Ch4:
                return AnyView(NavigationLink (destination: iOSApp17DevelopmentEssentialsCh4()) {
                    Text(dataiOSApp17DevelopmentEssentials.title)
                })
                //じっくり16で追加
            case .Ch5:
                return AnyView(NavigationLink (destination: iOSApp17DevelopmentEssentialsCh5()) {
                    Text(dataiOSApp17DevelopmentEssentials.title)
                })
                //じっくり17で追加
            case .Ch6:
                return AnyView(NavigationLink (destination: iOSApp17DevelopmentEssentialsCh6()) {
                    Text(dataiOSApp17DevelopmentEssentials.title)
                })
                //じっくり18で追加
            case .Ch7:
                return AnyView(NavigationLink (destination: iOSApp17DevelopmentEssentialsCh7()) {
                    Text(dataiOSApp17DevelopmentEssentials.title)
                })
                //じっくり19で追加
            case .Ch8:
                return AnyView(NavigationLink (destination: iOSApp17DevelopmentEssentialsCh8()) {
                    Text(dataiOSApp17DevelopmentEssentials.title)
                })
                //じっくり20、21で追加
            case .Ch9:
                return AnyView(NavigationLink (destination: iOSApp17DevelopmentEssentialsCh9()) {
                    Text(dataiOSApp17DevelopmentEssentials.title)
                })
                //じっくり22、23で追加
            case .Ch10:
                return AnyView(NavigationLink (destination: iOSApp17DevelopmentEssentialsCh10()) {
                    Text(dataiOSApp17DevelopmentEssentials.title)
                })
                //じっくり24で追加
            case .Ch11:
                return AnyView(NavigationLink (destination: iOSApp17DevelopmentEssentialsCh11()) {
                    Text(dataiOSApp17DevelopmentEssentials.title)
                })
                //じっくり25で追加
            case .Ch12:
                return AnyView(NavigationLink (destination: iOSApp17DevelopmentEssentialsCh12()) {
                    Text(dataiOSApp17DevelopmentEssentials.title)
                })
                //じっくり26で追加
            case .Ch13:
                return AnyView(NavigationLink (destination: iOSApp17DevelopmentEssentialsCh13()) {
                    Text(dataiOSApp17DevelopmentEssentials.title)
                })
                //じっくり27,28で追加
            case .Ch14:
                return AnyView(NavigationLink (destination: iOSApp17DevelopmentEssentialsCh14()) {
                    Text(dataiOSApp17DevelopmentEssentials.title)
                })
                //じっくり29で追加
            case .Ch15:
                return AnyView(NavigationLink (destination: iOSApp17DevelopmentEssentialsCh15()) {
                    Text(dataiOSApp17DevelopmentEssentials.title)
                })
            }
        }
    }
     #Preview  {
        iOSApp17DevelopmentEssentials()
    }
    

    ◆TitleManageFile.swift(追加分)

    let essentialsChapter15NavigationTitle = "第15章"
    let essentialsChapter15Title = "第15章 エラーハンドリング"
    let essentialsChapter15SubTitle = "第1節 エラーハンドリング"

    ◆CodeManageFile.swift(追加分)

    let codeEssentials15 = """
    /**:-------------------
     Essentials 第15章 エラーハンドリング
     ---------------------*/
    //変数を宣言
    var warning15_3 = false
    var wrong15_3 = false
    var fatal15_3 = true
    var errorWarningMsg15_3 = "Warn"
    var errorWrongMsg15_3 = "Wrong"
    var errorFatalMsg15_3 = "Fatal"
    //エラーコード
    var errorCode15_6 = 3
    
    //エラーを宣言
    enum ErrorTypes15_1: Error{
        case warning
        case wrong
        case fatal
    }
    //エラー処理関数
    func errorHandling15_2() throws {
        guard warning15_3 == true else {
            throw ErrorTypes15_1.warning
        }
        guard wrong15_3 == true else {
            throw ErrorTypes15_1.wrong
        }
        guard fatal15_3 == true else {
            throw ErrorTypes15_1.fatal
        }
    }
    //try-catch文
    //3つとも場合分け
    func returnErrorHandling15_4() -> String{
        do {
            try errorHandling15_2()
        } catch ErrorTypes15_1.warning {
            return("Error is warning")
        } catch ErrorTypes15_1.wrong{
            return("Error is wrong")
        } catch ErrorTypes15_1.fatal {
            return("Error is fatal")
        } catch {
            return("Error is unknown")
        }
        return("Error is nothing")
    }
    //fatalかどうかで場合分け
    func returnErrorHandling15_5() -> String{
        do {
            try errorHandling15_2()
        } catch ErrorTypes15_1.warning, ErrorTypes15_1.wrong {
            return("Error is not fatal")
        } catch ErrorTypes15_1.fatal {
            return("Error is fatal")
        } catch {
            return("Error is unknown")
        }
        return("Error is nothing")
    }
    //3つとも場合分け
    func returnErrorHandling15_6() -> String{
        defer{
            //初期値の3に戻す
            errorCode15_6 = 3
        }
        do {
            try errorHandling15_2()
        } catch ErrorTypes15_1.warning {
            errorCode15_6 += 100
            print("Error is warning,ErrorCode: \\(errorCode15_6)")
            return("Error is warning")
        } catch ErrorTypes15_1.wrong{
            errorCode15_6 += 2000
            print("Error is wrong,ErrorCode: \\(errorCode15_6)")
            return("Error is wrong")
        } catch ErrorTypes15_1.fatal {
            errorCode15_6 += 10000
            print("Error is fatal,ErrorCode: \\(errorCode15_6)")
            return("Error is fatal")
        } catch {
            print("Error is unknown")
            return("Error is unknown")
        }
        print("Error is nothing")
        return("Error is nothing")
    }
    //実行
    returnErrorHandling15_6()
    //3つとも場合分け:エラーコードのみ
    func returnErrorHandling15_7() -> String{
        defer{
            errorCode15_6 = 3
        }
        do {
            try errorHandling15_2()
        } catch ErrorTypes15_1.warning {
            errorCode15_6 += 100
            print("ErrorCode: \\(errorCode15_6)")
            return("Error is warning")
        } catch ErrorTypes15_1.wrong{
            errorCode15_6 += 2000
            print("ErrorCode: \\(errorCode15_6)")
            return("Error is wrong")
        } catch ErrorTypes15_1.fatal {
            errorCode15_6 += 10000
            print("ErrorCode: \\(errorCode15_6)")
            return("Error is fatal")
        } catch {
            print("Error is unknown")
            return("Error is unknown")
        }
        print("Error is nothing")
        return("Error is nothing")
    }
    //実行
    returnErrorHandling15_7()
    for n in 1...100{
        returnErrorHandling15_7()
    }
    """

    ◆PointManageFile.swift(追加分)

    let pointEssentials15 = """
    ・無意識に手が当たって、何かのコードが1文字消えた
    ↓
    ・バグ警告でバグfixできたので、表示上はバグが消えた:実際にはこれで8割ぐらいは解消できるんだけど、、、
    ↓
    ・実際に動かしてみたら、全然想定通りに動かないか、動的エラー発生
    みたいな感じで、頭だけで書いたコードで、警告も出てないからOKで一回も検証しないままリリースとかすると後から涙目😢になるからね。
    
    ここでポイント①:エラーハンドリングを理解する
    1.iOS アプリのメソッド内で望ましい結果が得られない場合にエラーをトリガー (またはスロー) する
    2.メソッドによってスローされたエラーをキャッチして処理する
    ↓
    さらに、エラーがスローされた場合
    ・そのエラーは特定のタイプのエラーであり、これを使用してエラーの特定の性質を識別し、取るべき最も適切なアクションを決定できる。
    ・エラータイプの値には、エラー プロトコルに準拠する任意の値を指定できる。
    """

    ◆URLManageFile.swift(追加分)

    let urlEssentials15 = "https://note.com/m_kakudo/n/nc0273cc3a5f5"

    以上。

     
     
    タダの道楽者💫ネガティブなコメント一切お断り 裾野が広がりゃそれで良い 洋書と古典とSwiftをこよなく愛するオッさん👀 色即是空🕺 記事はマガジンの方が読みやすいかな💦 ちょい難とダラダラ💃 無駄な対立軸と課題探し思考は要らんな。 変幻自在➕縦横無尽➕融通無碍🟰天衣無縫

    あなたへのおすすめ