こんにちは。きんくまです。
ブログのアクセスログを読むと、iOSのエラーハンドリングの記事がそこそこアクセスがあります。
ただ、この書き方だとswift2だと動かないので申し訳なく思いまして。なのでswift2版を書いておこうかと。
2番目の記事のエラーと例外のところはswiftでも考え方は同じなので役に立つかと思われます。
>> [iOS] swiftでもエラーハンドリングしたい!
>> [iOS] NSErrorでエラーハンドリングしたい
ちなみにこの文はAppleさんの公式のドキュメント見ながら書いているので、詳しくはそこを読んでいただければ。
>> The Swift Programming Language (Swift 2.2): Error Handling
手短に書き方だけ説明。do catch 構文
まずは使い方です。この構文を知っておけばとりあえずはなんとかなります。
do { try xxxxx() } catch { print("error") }
エラーの内容がしりたければこちら
do { try xxxxx() } catch let error as NSError { print("error \(error)") }
xxxxx のところで throws を返すかもしれないメソッドを呼び出してください。
こういうやつです。
func xxxxx() throws
もしエラーがそこで投げられたら catch 構文が呼ばれます。
エラーを自分で作る
ここからはエラーを自分で作ってみます。
今回のお題
割り算をしたときのエラーを処理する
割り算関数はこうしました。num1をnum2で割った答えを返します。
func divide(num1:Double, num2:Double) throws -> Double? { return num1 / num2 }
いまの状態だと何のチェックもなく値を返しています。ここで2つのエラーを考えてみます。
1. 数値を0で割ったらエラー(つまり num2 == 0) 2. 2つの引数が num1 < num2 だったらエラー
2番目の条件はふつうは全く必要ありませんが、今回説明するためにたまたまそういう仕様だったと思ってください
エラーを定義します。enum でまとめて、個別のエラーは case で書きます。
enum CalculationError: ErrorType { case DivideByZero case Num1LessThanNum2 }
エラーを投げる
さきほどのdivide関数を、引数によってエラーを投げるようにしてみます
func divide(num1:Double, num2:Double) throws -> Double? { guard num2 != 0 else { throw CalculationError.DivideByZero } guard num1 >= num2 else { throw CalculationError.Num1LessThanNum2 } return num1 / num2 }
guard というキーワードの構文が出てきました。guard文は引数のチェックなどに使うと便利な構文です。エラーハンドリング以外のときでも使えます。
guard エラーにならない条件 else { エラーになったらこの中に入る //この中で return, break, continue, throw のいずれかを呼ぶ必要がある }
こんな感じに使うと便利みたいです。
func greeting(message:String?) -> String? { guard let myMessage = message else { return nil } //ここから先はMyMessageはアンラップされた状態でnilじゃないことが保証されます return "\(myMessage) world!" } let text:String = "hello" let greetResult:String? = greeting(text) //hello world let text2:String? = nil let greetResult2:String? = greeting(text2) //nil
参考
>> Swift 2.0 で追加された guard のいいところ - Qiita
構文で1点注意するところがあり、guard のあとはエラーに「なる」条件ではなくエラーに「ならない」条件を書くということです。guard文と同じことはif文を使っても書くことができます。さきほどの divide を if 文でチェックしてみます。条件式がguardのときと逆になっていることにチェックです。
func divide(num1:Double, num2:Double) throws -> Double? { if num2 == 0 { throw CalculationError.DivideByZero } if num1 < num2 { throw CalculationError.Num1LessThanNum2 } return num1 / num2 }
guardのとき
guard num2 != 0 else {} guard num1 >= num2 else {}
ifのとき
if num2 == 0 {} if num1 < num2 {}
if文の方が、エラーのときの条件式のままなのでわかりやすいかなーとも思います。
エラーを投げる関数を使う
var n1:Double = 1 var n2:Double = 2 var result:Double? do { result = try divide(n1, num2: n2) } catch CalculationError.DivideByZero { print("Calculation Error: num2 is zero") } catch CalculationError.Num1LessThanNum2 { //この場合はここが呼ばれる print("Calculation Error: num1 \(n1) < num2 \(n2)") } catch { print("unexpected error") } print("result \(result)")
上の例文では n1 < n2 なので 2番目の catch が呼ばれます。 n1, n2 を適当な値に変更したときの挙動を試してみるといいかもです。(n2 = 0 など)
2016/04/07 追記
do catch のところは catch文を全部書かなくてもこれでもいけました
do { result = try divide(n1, num2: n2) } catch let error as CalculationError { print("CalculationError: \(error)") //CalculationError: Num1LessThanNum2 //Appleで推奨してない書き方だけど、上でエラーのカテゴリをざっくりわけたあとに振り分けも可能 switch error { case CalculationError.DivideByZero: print("switch DivideByZero") break case CalculationError.Num1LessThanNum2: print("switch Num1LessThanNum2") break default: break } } catch { print("unexpceted Error'") }
まとめ
swift2でのエラーハンドリングでした。do catch, guard などの使い方をチェックですね。
今回のサンプルコード全文。(Playgroundで試しました)
import UIKit enum CalculationError: ErrorType { case DivideByZero case Num1LessThanNum2 } func divide(num1:Double, num2:Double) throws -> Double? { guard num2 != 0 else { throw CalculationError.DivideByZero } guard num1 >= num2 else { throw CalculationError.Num1LessThanNum2 } return num1 / num2 } var n1:Double = 3 var n2:Double = 0 var result:Double? do { result = try divide(n1, num2: n2) } catch CalculationError.DivideByZero { print("Calculation Error: num2 is zero") } catch CalculationError.Num1LessThanNum2 { print("Calculation Error: num1 \(n1) < num2 \(n2)") } catch { print("error") } print("result \(result)")
■ 自作iPhoneアプリ 好評発売中!
・フォルメモ - シンプルなフォルダつきメモ帳
・ジッピー電卓 - 消費税や割引もサクサク計算!
■ LINEスタンプ作りました!
毎日使える。とぼけたウサギ