Swift 2.0 の Error Handling #yhios

>100 Views

June 21, 15

スライド概要

Swift 2.0 で新登場した Error Handling についての特徴や使い方などを、これまでの Swift 1.x にもあったその他のエラー処理の方法と合わせて紹介してみました。

※ Docswell での公開に移行する直前の Slideshare での閲覧数は 6,804 でした。

profile-image

正統派趣味人プログラマー。プログラミングとは幼馴染です。

シェア

またはPlayer版

埋め込む »CMSなどでJSが使えない場合

関連スライド

各ページのテキスト
1.

Swift 2.0  の ERROR HANDLING 2015.06.20   @  横浜へなちょこ  iOS  勉強会  #35 EZ-‐‑‒NET  熊⾕谷友宏   http://ez-‐‑‒net.jp/

2.

熊谷友宏 http://ez-net.jp/ @es̲kumagai Xcode 5 徹底解説 MOSA IP Phone 音でダイヤル いつもの電卓 with 割勘ウォッチ 音で再配達ゴッド

3.

Error Handling ってなに?

4.

NSError を 使いやすくするってコト!

5.

エラーの話

6.

これまでの NSError Swift 1.2 たとえば NSFileManager

7.

これまでの NSError Swift 1.2 // オプショナルな NSError を状態として用意 var error:NSError? = nil // 用意した NSError を inout で渡す let succeeded = fm.removeItemAtPath(path, error: &error) // エラーを検査する if !succeeded, let error = error { }

8.

これまでの NSError Swift 1.2 func removeItemAtPath( path:String, error: NSErrorPointer) -> BOOL ▶ 戻り値で目的の結果を返す ▶ エラーのときは NSErrorPointer で詳細を返す ▶ つまりエラーの詳細を知りたければ NSError? を事前に別途用意する

9.

これまでの NSError Swift 1.2 func contentsOfDirectoryAtPath( path:String, error: NSErrorPointer) -> [AnyObject]? ▶ 戻り値で目的の結果を返す ▶ コンテンツが無ければ空の配列を返す ▶ エラーがあったときは ▶ 戻り値で nil を返す ▶ NSErrorPointer で詳細情報を返す

10.

これからの NSError Swift 2.0

11.

これからの NSError Swift 2.0 // 正常処理のスコープを決める do { // 目的をまっすぐ達成する try fm.removeItemAtPath(path) } catch let error as NSError { // エラーならここで処理する }

12.

これからの NSError Swift 2.0 func removeItemAtPath( path:String) throws -> Void ▶ 目的を遂行する ▶ エラーが発生するかもネ!

13.

これからの NSError Swift 2.0 func contentsOfDirectoryAtPath( path:String) throws -> [String] ▶ 戻り値で目的の結果を返す ▶ コンテンツが無ければ空の配列を返す ▶ エラーが発生するかもネ!

14.

Error Handling で 正常系とエラー系とを分離

15.

ところで これまでの NSError は

16.

これまでの NSError Swift 1.2 // NSError を用意しなくても実行可能 fm.removeItemAtPath(path, error: nil) ▶ 成否を戻り値だけで判定する ▶ 成功したものとして突き進むも可能

17.

それって安全じゃない

18.

Swift は そんなことはさせません

19.

Swift は「安全」がお好き

20.

たとえば func setAttributes(attributes, ofItemAtPath:path) throws

21.

Error Handling Swift 2.0 エラーを未想定ならエラー fm.setAttributes(attr, ofItemAtPath:path)

22.

Error Handling Swift 2.0 エラーを想定! try fm.setAttributes(attr, ofItemAtPath:path)

23.

Error Handling Swift 2.0 エラーを想定! try fm.setAttributes(attr, ofItemAtPath:path) 正常系はこれ以降のスコープ全体

24.

Error Handling Swift 2.0 正常系のスコープを明記! do { try fm.setAttributes(attr, ofItemAtPath:path) }

25.

Error Handling Swift 2.0 正常系のスコープを明記! do { try fm.setAttributes(attr, ofItemAtPath:path) 正常時の処理をこの中で決着する }

26.

Error Handling Swift 2.0 do { try fm.setAttributes(attr, ofItemAtPath:path) } catch let error as NSError { エラーが発生したらキャッチ! }

27.

Error Handling Swift 2.0 do { try fm.setAttributes(attr, ofItemAtPath:path) } catch let error as NSError { エラーが発生したらキャッチ! エラー時の処理をこの中で決着する }

28.

Error Handling Swift 2.0 do { try fm.setAttributes(attr, ofItemAtPath:path) エラー時はここは処理されない } catch let error as NSError { エラーが発生したらキャッチ! エラー時の処理をこの中で決着する }

29.

まとめると

30.

Error Handling Swift 2.0 正常系のスコープを明記! do { エラーを想定! try fm.setAttributes(attr, ofItemAtPath:path) 正常時の処理をこの中で決着する エラー時はここは処理されない } catch let error as NSError { エラーが発生したらキャッチ! エラー時の処理をこの中で決着する }

31.

つまり

32.

エラーを確実に扱えるってコト!

33.

next

34.

絶対エラーにならなくない?

35.

絶対にエラーにならない 場合だってあるかもしれない

36.

これまでの NSError Swift 1.2

37.

これまでの NSError Swift 1.2 // エラーチェックを記載しない fm.setAttributes(attr, ofItemAtPath:path, error:nil) 成否に関わらず以下が実行される

38.

Error Handing Swift 2.0

39.

Error Handling Swift 2.0 // エラーはない ! と明記する try! fm.setAttributes(attr, ofItemAtPath:path) エラーのときは以下に進まず 強制終了

40.

つまり

41.

無視するにも 覚悟が要るってコト!

42.

next

43.

エラーのときの後始末

44.

Error Handling do { 処理が終わったら閉じたい let handle = try file.open() try fm.setAttributes(attr, ofItemAtPath:path) でもエラーが発生すると… ここまでたどり着けない handle.close() } catch { }

45.

Error Handling do { 処理が終わったら閉じたい let handle = try file.open() 最後に処理したいことを先に書く defer { handle.close() } try fm.setAttributes(attr, ofItemAtPath:path) ここでエラーが発生しても… ブロックを抜ける直前に処理される } catch { }

46.

余談

47.

もしも Swift の Error Handling が @try-catch-finally だったとしたら

48.

もし try-finally だったとしたら… var stream:Stream? = nil @try { 初期化と後始末のスコープ分断を考慮して外側に定義 stream = Stream.open(path) fm.setAttributes(attr, ofItemAtPath:path) そもそも、どこでエラーが起こるの…? } @catch let error { } @finally { 未初期化を考慮してオプショナルチェイニング stream?.close() }

49.

つまり

50.

こうではなく var stream:Stream? = nil @try { stream = Stream.open(path) fm.setAttributes(attr, ofItemAtPath:path) } @catch let error { } @finally { stream?.close() } 入れ物の事前準備が必要 ▶ 3つのブロックに着目 ▶ 流れよりも構文が主役 ▶ どこでエラーになるかが コードから読めない ▶

51.

こうなる do { let handle = try file.open() defer { handle.close() } try fm.setAttributes(attr, ofItemAtPath:path) } catch { } 事前準備が不要 ▶ 流れが主役 原則成功、ときどき失敗 ▶ コードからエラーが 発生する箇所がわかる ▶

52.

Error Handling は 美しいってコト!

53.

next

54.

Swift でエラーを扱う方法

55.

Optional<T>

56.

Optional<T> 値があるかないかを扱う型 ▶ 値の有無による判断を強制 ▶ 値がないときがエラーとは限らない if let value = optional { } else { // 値がなかったときにエラーとするかは状況次第 }

57.

Optional のイメージ ?

58.

Optional<T> 使いどころ ▶ 単純に値の有無を提示する ▶ エラーかどうかを決め付けない // 例えば、リストの中から値を検索する関数 func find(list, value) -> Index? { }

59.

いわゆる Either 型

60.

いわゆる Either 型 どちらかの状況を表現する型 ▶ 成功値か失敗値かを取る列挙型 ▶ 成否というより状況の切り分けに着目 enum Result<T> { case Success(T) case Failure(Error) }

61.

いわゆる Either 型 どちらかの状況を表現する型 ▶ 戻り値ひとつで状況の切り分けが可能 ▶ 両者を同じ重みで扱う switch getResult() { case .Success(let value): case .Failure(let error): }

62.

Either 型のイメージ

63.
[beta]
いわゆる Either 型
使いどころ
▶ 戻り値で二つの場面を提示する
▶ 背反する分岐点を表現する

// 成功したか失敗したかで進路を分岐する

enum Result<T,U> {
case Succeeded<T>
case Failed<U>
}

switch getResult() {
case .Success(let value):
case .Failure(let error):
}

64.

Fatal Error

65.

Fatal Error 致命的なエラー ▶ 処理が継続できない状況を表現 ▶ 想定外を持ち越さない fatalError("もうムリ…") 以降の処理は実行させない!

66.

Fatal Error のイメージ

67.

Fatal Error 使いどころ ▶ Optional で絶対に値が入っているとき ▶ Optional で値が入っていないと困るとき ▶ try が絶対にエラーにならないとき ▶ 処理を継続できないと判断したとき ▶ 相手に責任を取らせたいとき などなど 活用の場面は幅広い

68.

実際

69.

Fatal Error Swift でも積極的に使われている // 強制アンラップ let value = optional! 値がなければ fatalError // 暗黙アンラップなオプショナル var value:String! nil が入っているのに操作したら fatalError

70.

Fatal Error Swift でも積極的に使われている // 強制キャスト let subObj = obj as! SubClass キャストできなければ fatalError // エラーを想定しない try! execute() エラーが起これば fatalError

71.

Fatal Error で 想定外を想定内へ転換する

72.

New! Swift の Error Handling

73.

エラー型の定義

74.

Error Handling ErrorType プロトコル ▶ エラー型を ErrorType で表現 ▶ 実装を求められないプロトコル ▶ 列挙型と NSError が準拠できる protocol ErrorType { }

75.

Error Handling Error 型 ▶ エラー型は 列挙型 で表現 ▶ 列挙型を ErrorType に準拠させる ▶ 起こり得るエラーを列記 enum OpenError : ErrorType { case NotFound case Readonly case Busy(reason:String) } 値付き列挙子も使える

76.

関数やメソッドで使う

77.

Error Handling エラーを示唆する ▶ エラーが有り得る機能に throws を付与 ▶ エラーになるかもしれないことが プログラマーとコンパイラの両者に伝わる func open(file:FILE) throws -> Stream { : :

78.

Error Handling エラーを通知する ▶ エラーは throw で通知する ▶ エラーは列挙子で指定する ▶ throws を指定した機能でだけ通知可能 func open(file:FILE) throws -> Stream { guard _exists(file) else { throw OpenError.NotFound } : :

79.

使う側は 確実にエラーを想定

80.

Error Handling エラーを想定する ▶ 正常処理の範囲を do で表現 ▶ エラーが発生し得る場所に try を明記 ▶ エラーは catch で補足する do { let stream = try open(file) } catch { } エラーに挑む!

81.

Error Handling 全てのエラーに対処する do { let stream = try open(file) } catch OpenError.NotFound { } catch OpenError.ReadOnly { } catch OpenError.Busy(let reason) { }

82.

Error Handling 全てのエラーに対処する do { let stream = try open(file) } catch OpenError.Busy { 値付き列挙子の値を加味しないことも可能 } catch is OpenError { 列挙子を加味せずに捕獲することも可能 }

83.

おさらい

84.

Error Handling おさらい ▶ エラーを列挙型で表現 起こり得るエラーが一目瞭然 ▶ エラーの可能性を throws で示唆 プログラマーとコンパイラに意思が伝わる ▶ エラーが起こり得る箇所に try を明記 どこでエラーを想定しているかが明確 ▶ エラーを想定したコードが必須 強制されると悩まずに済むので楽になる

85.

つまり

86.

Error Handling で とっても楽になるってコト!

87.

Error Handling のイメージ

88.

Error Handling 使いどころ ▶ 達成すべき目的があり それを達成できない可能性があるとき ▶ 原因が実行時エラーに限られるとき ▶ 原因がいくつか考えられるとき ▶ 原因を提示し、対応を求めたいとき // 目的が明確で、エラーも有り得る複合的な機能 func open(file:FILE) throws -> Stream

89.

まとめ

90.

エラーを扱う手段 ? Optional 型 ▶ 単純に値の有無を提示 いわゆる Either 型 ▶ 戻り値で分岐点を提示 Fatal Error ▶ 強制終了して根本的な改善を迫る Error Handling ▶ 目的を遂行できない時に原因を提示

91.

Swift はこれらの使用を強要する 強要されると プログラミングが楽になる ?

92.

つまり

93.

Swift は すごいってコト!

94.

おしまい。 ▶ Error Handling ってなに? ▶ NSError を使いやすくするってコト! ▶ エラーを確実に扱えるってコト! ▶ 無視するにも覚悟が要るってコト! ▶ Error Handling は美しいってコト! ▶ Error Handling でとっても楽になるってコト! ▶ Swift はすごいってコト!

95.

おまけ

96.

Objective-C のことも Swift は見捨てない

97.

Objective-C からの自動変換

98.

Objective-C からの自動変換 末尾の NSError を throws に変換 ▶ 戻り値がクラスの場合 ▶ 最後の引数が NSError** の場合 // このような Objective-C コードが - (NSString*)getNameFromPath:(NSString*)path error:(NSError**)error; // このような Swift コードになる func getNameFromPath(path:String!) throws -> String

99.

Objective-C からの自動変換 末尾の NSError を throws に変換 ▶ 戻り値が BOOL の場合 ▶ 最後の引数が NSError** の場合 // このような Objective-C コードが - (BOOL)prepareWithOptions:(NSArray*)opts error:(NSError**)error; // このような Swift コードになる func prepareWithOptions(opts:[AnyObject]!) throws -> Void

100.

Objective-C への自動変換

101.

Objective-C への自動変換 throws を NSError に変換 ▶ 戻り値が @objc 互換オブジェクトの場合 ▶ throws が指定されている場合 // このような Swift コードが func getName(path:String) throws -> String // このような Objective-C コードになる - (NSString*)getName:(NSString*)path error:(NSError**)error;

102.

Objective-C への自動変換 throws を NSError に変換 ▶ 戻り値が Void の場合 ▶ throws が指定されている場合 // このような Swift コードが func prepare(options:[String]?) throws // このような Objective-C コードになる - (BOOL)prepare:(NSArray**)options error:(NSError**)error;

103.

安心して Error Handling を活用できる

104.

つまり

105.

Swift は かっこいいってコト!

106.

おしまい。