6
4

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

どんなエラーが起こったかユーザーに伝えたいということで、、、

Last updated at Posted at 2023-11-03

流れ

  1. どうして勉強したいと思ったのか?
  2. キーワード
  3. エラー処理するための準備
  4. 実際にメソッドを実行してみる
  5. 伝播と捕捉

どうして勉強したいと思ったのか?

アプリを作っている過程で、処理が想定通りに進まないなということが出てきた場合に、ユーザーにエラーの内容を伝えたいと思いエラー処理について勉強しました。今回はenumError型(今回はLocalizedError)に適合させてそのcaseでユーザーにエラーの内容を伝える方法を記述していきたいと思います。

今回はこんな感じでアラートを表示させます。

スクリーンショット 2023-11-03 21.12.51.png

キーワード

『do』
『catch』
『throw ・ throws』
『try』
『LocalizedError』
『alert(isPresented:error:actions:message:)』 SwiftUIのインスタンスメソッド

エラー処理するための準備

enumLocalizedErrorを適合させます。LocalizedErrorに適合させる理由としては,今回使用するアラートalert(isPresented:error:actions:message:)インスタンスメソッドのerrorの引数は、このLocalizedErrorに適合した引数を受け取るためです。

enum TextError: LocalizedError {
    case parseError
    case emptyError
    case unknown
    var errorDescription: String? {
        switch self {
        case .parseError:
            return "数字を入力してください"
        case .emptyError:
            return "入力されていません"
        case .unknown:
            return "原因がわかりません"
        }
    }
}

次に入力値をチェックするためのメソッドを記述します。

  1. この関数はエラーを投げる可能性があるため、引数リストの直後にthrowsキーワードを記述します。
  2. もしも引数numTextが空だった場合、throw文のTextError.emptyErrorを実行し、エラーを投げます。numParseメソッドもパースできなかった場合にエラーを投げるよう文を記述します。
  3. 全てのチェックを完了したら、プロパティnum(Int型)を返します。
func parseNumberFromString(numText: String) throws -> Int {
        guard !numText.isEmpty else {
            throw TextError.emptyError
        }
        let numParse: (String) -> Int? = { str in Int(str) }
        guard let num = numParse(numText) else {
            throw TextError.parseError
        }
        return num
    }

実際にメソッドを実行してみる。

上で作ったメソッドを動かしてみます。ここで重要になってくるのは、2つです。

  • 『do - try - catch』
  • 『.alert(isPresented: $showError, error: errorType)』

doスコープ内は成功した結果を返してくれます。ここで忘れてはいけないのが、エラーを投げる関数の前にtryキーワードをつけるということです。
catchスコープ内は投げられたエラーをキャッチしてくれます。このスコープ内では、投げられたエラーの値はerrorという名前で参照が可能です。print(error)で中身がみれます。今回のサンプルコードでは最後のcatchスコープには、errorをTextError型にキャストしていないものがあります。これは、未知のerrorが返された時のために、不明なエラーとして、errorTypeに.unknownを代入しています。
.alert(isPresented: $showError, error: errorType)メソッドには、error引数に対して、TextError型のerrorTypeを渡します。

struct ErrorTest: View {
    
    @State var numText = ""
    @State var errorType: TextError? = nil
    @State var num: Int? = nil
    @State var showError = false
    
    var body: some View {
        ZStack{
            Color.blue.ignoresSafeArea()
            VStack{
                TextField("", text: $numText)
                    .textFieldStyle(.roundedBorder)
                    .padding()
                Button("解析"){
                    do {
                        num =  try parseNumberFromString(numText: numText)
                    } catch let error as TextError {
                        showError = true
                        errorType = error
                    } catch {
                        showError = true
                        errorType = .unknown
                    }
                }
                .foregroundColor(.white)
                .padding()
                VStack{
                    if let num = num {
                        Text("数字は:\(num)です")
                    } else {
                        Text("数字なし")
                    }
                }
                .foregroundColor(.white)
                .padding()
            }
            .alert(isPresented: $showError, error: errorType) {
                Button("承知"){
                    //...処理を記述
                }
            }
        }
    }
}

伝播と捕捉

下のサンプルコードはエラーの伝播捕捉を表すために書きました。まず初めに、捕捉とは、エラーの発生を把握して対応する手続きを起動することを指します。f1メソッドを呼ぶとf2メソッドが呼ばれ、f2メソッドを呼ぶとf3メソッドが呼ばれるという仕組みになっています。そして、f3メソッドはエラーを投げます。このときf3メソッドではエラーの捕捉(キャッチ)は起きません。最初の呼び出し元まで遡って伝播していきます。この時throwtryが書かれた文の上のコードは実行されますが、下のコードは実行されません。do - catchでエラーを捕捉できれば、対応するcatchスコープの処理が実行される仕組みになっています。


enum ErrorType: Error {
    case one
    case two
    case three
}

do {
   try f1()
} catch let error as ErrorType {
    print(error) //⭐️three
}  catch {
    print("unknown")
}

func f1() throws {
    print("実行される")
    try f2()
    print("実行されない")
}

func f2() throws {
    try f3()
}

func f3() throws {
    let num:Int? = nil
    guard let num = num else {
        print("実行される")
        throw ErrorType.three
        print("実行されない")
    }
}

まとめ

  • エラーを適切に処理し、ユーザーに理解しやすいメッセージを提供することで、ユーザーはどのように対処すればよいのか理解することができると思います。ユーザーの満足度を上げるためにもこれからリリースするアプリにしっかり導入していきたいと思います。突然クラッシュなんかしたら、ユーザーはびっくりしますよね。。。
  • エラー処理を記述することで、開発していく過程でどこでエラーが起こっているのか理解しやすくなりました。アプリを開発していく上で必須だなと感じました。
  • 勉強途中で至らぬところは是非ご指摘して頂けたらと思います。

参考書籍

解読swift第5版

6
4
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
6
4

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?