流れ
- どうして勉強したいと思ったのか?
- キーワード
- エラー処理するための準備
- 実際にメソッドを実行してみる
- 伝播と捕捉
どうして勉強したいと思ったのか?
アプリを作っている過程で、処理が想定通りに進まないなということが出てきた場合に、ユーザーにエラーの内容を伝えたいと思いエラー処理について勉強しました。今回はenum
をError
型(今回はLocalizedError
)に適合させてそのcase
でユーザーにエラーの内容を伝える方法を記述していきたいと思います。
今回はこんな感じでアラートを表示させます。
キーワード
『do』
『catch』
『throw ・ throws』
『try』
『LocalizedError』
『alert(isPresented:error:actions:message:)』 SwiftUIのインスタンスメソッド
エラー処理するための準備
enum
にLocalizedError
を適合させます。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 "原因がわかりません"
}
}
}
次に入力値をチェックするためのメソッドを記述します。
- この関数はエラーを投げる可能性があるため、引数リストの直後に
throws
キーワードを記述します。 - もしも引数numTextが空だった場合、
throw
文のTextError.emptyErrorを実行し、エラーを投げます。numParseメソッドもパースできなかった場合にエラーを投げるよう文を記述します。 - 全てのチェックを完了したら、プロパティ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メソッドではエラーの捕捉(キャッチ)は起きません。最初の呼び出し元まで遡って伝播していきます。この時throw
とtry
が書かれた文の上のコードは実行されますが、下のコードは実行されません。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("実行されない")
}
}
まとめ
- エラーを適切に処理し、ユーザーに理解しやすいメッセージを提供することで、ユーザーはどのように対処すればよいのか理解することができると思います。ユーザーの満足度を上げるためにもこれからリリースするアプリにしっかり導入していきたいと思います。突然クラッシュなんかしたら、ユーザーはびっくりしますよね。。。
- エラー処理を記述することで、開発していく過程でどこでエラーが起こっているのか理解しやすくなりました。アプリを開発していく上で必須だなと感じました。
- 勉強途中で至らぬところは是非ご指摘して頂けたらと思います。