106
EZNET 熊友宏 http://eznet.jp/ Swift 2.0 2015.06.20 @ 横浜へなちょこ iOS 勉強会 #35 ERROR HANDLING

Swift 2.0 の Error Handling #yhios

Embed Size (px)

Citation preview

Page 1: Swift 2.0 の Error Handling #yhios

EZ-‐‑‒NET  熊⾕谷友宏  http://ez-‐‑‒net.jp/

Swift 2.0  の

2015.06.20  @  横浜へなちょこ  iOS  勉強会  #35

ERROR HANDLING

Page 2: Swift 2.0 の Error Handling #yhios

熊谷友宏http://ez-net.jp/ @es_kumagai

Xcode 5 徹底解説

IP Phone 音でダイヤル 音で再配達ゴッドいつもの電卓 with 割勘ウォッチ

MOSA

Page 3: Swift 2.0 の Error Handling #yhios

Error Handling ってなに?

Page 4: Swift 2.0 の Error Handling #yhios

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

Page 5: Swift 2.0 の Error Handling #yhios

エラーの話

Page 6: Swift 2.0 の Error Handling #yhios

これまでの NSErrorSwift 1.2

たとえば NSFileManager

Page 7: Swift 2.0 の Error Handling #yhios

これまでの NSErrorSwift 1.2

// オプショナルな NSError を状態として用意 var error:NSError? = nil

// 用意した NSError を inout で渡す let succeeded = fm.removeItemAtPath(path, error: &error)

// エラーを検査する if !succeeded, let error = error {

}

Page 8: Swift 2.0 の Error Handling #yhios

func removeItemAtPath( path:String, error: NSErrorPointer) -> BOOL

▶ 戻り値で目的の結果を返す ▶ エラーのときは

NSErrorPointer で詳細を返す ▶ つまりエラーの詳細を知りたければ

NSError? を事前に別途用意する

これまでの NSErrorSwift 1.2

Page 9: Swift 2.0 の Error Handling #yhios

func contentsOfDirectoryAtPath( path:String, error: NSErrorPointer) -> [AnyObject]?

▶ 戻り値で目的の結果を返す ▶ コンテンツが無ければ空の配列を返す ▶ エラーがあったときは

▶ 戻り値で nil を返す ▶ NSErrorPointer で詳細情報を返す

これまでの NSErrorSwift 1.2

Page 10: Swift 2.0 の Error Handling #yhios

これからの NSErrorSwift 2.0

Page 11: Swift 2.0 の Error Handling #yhios

これからの NSErrorSwift 2.0

// 正常処理のスコープを決める do {

// 目的をまっすぐ達成する try fm.removeItemAtPath(path) } catch let error as NSError {

// エラーならここで処理する }

Page 12: Swift 2.0 の Error Handling #yhios

func removeItemAtPath( path:String) throws -> Void

▶ 目的を遂行する ▶ エラーが発生するかもネ!

これからの NSErrorSwift 2.0

Page 13: Swift 2.0 の Error Handling #yhios

func contentsOfDirectoryAtPath( path:String) throws -> [String]

▶ 戻り値で目的の結果を返す ▶ コンテンツが無ければ空の配列を返す ▶ エラーが発生するかもネ!

これからの NSErrorSwift 2.0

Page 14: Swift 2.0 の Error Handling #yhios

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

Page 15: Swift 2.0 の Error Handling #yhios

ところでこれまでの NSError は

Page 16: Swift 2.0 の Error Handling #yhios

これまでの NSErrorSwift 1.2

// NSError を用意しなくても実行可能 fm.removeItemAtPath(path, error: nil)

▶ 成否を戻り値だけで判定する ▶ 成功したものとして突き進むも可能

Page 17: Swift 2.0 の Error Handling #yhios

それって安全じゃない

Page 18: Swift 2.0 の Error Handling #yhios

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

Page 19: Swift 2.0 の Error Handling #yhios

Swift は「安全」がお好き

Page 20: Swift 2.0 の Error Handling #yhios

たとえば

func setAttributes(attributes, ofItemAtPath:path) throws

Page 21: Swift 2.0 の Error Handling #yhios

Error HandlingSwift 2.0

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

Page 22: Swift 2.0 の Error Handling #yhios

Error HandlingSwift 2.0

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

Page 23: Swift 2.0 の Error Handling #yhios

Error HandlingSwift 2.0

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

正常系はこれ以降のスコープ全体

Page 24: Swift 2.0 の Error Handling #yhios

Error HandlingSwift 2.0

do {

try fm.setAttributes(attr, ofItemAtPath:path)

}

正常系のスコープを明記!

Page 25: Swift 2.0 の Error Handling #yhios

Error HandlingSwift 2.0

do {

try fm.setAttributes(attr, ofItemAtPath:path)

}

正常時の処理をこの中で決着する

正常系のスコープを明記!

Page 26: Swift 2.0 の Error Handling #yhios

Error HandlingSwift 2.0

do {

try fm.setAttributes(attr, ofItemAtPath:path)

} catch let error as NSError {

}

エラーが発生したらキャッチ!

Page 27: Swift 2.0 の Error Handling #yhios

Error HandlingSwift 2.0

do {

try fm.setAttributes(attr, ofItemAtPath:path)

} catch let error as NSError {

}

エラーが発生したらキャッチ!

エラー時の処理をこの中で決着する

Page 28: Swift 2.0 の Error Handling #yhios

Error HandlingSwift 2.0

do {

try fm.setAttributes(attr, ofItemAtPath:path)

} catch let error as NSError {

}

エラーが発生したらキャッチ!

エラー時の処理をこの中で決着する

エラー時はここは処理されない

Page 29: Swift 2.0 の Error Handling #yhios

まとめると

Page 30: Swift 2.0 の Error Handling #yhios

Error HandlingSwift 2.0

do {

try fm.setAttributes(attr, ofItemAtPath:path)

} catch let error as NSError {

}

エラーが発生したらキャッチ!

エラー時の処理をこの中で決着する

エラー時はここは処理されない

正常時の処理をこの中で決着する

正常系のスコープを明記!

エラーを想定!

Page 31: Swift 2.0 の Error Handling #yhios

つまり

Page 32: Swift 2.0 の Error Handling #yhios

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

Page 33: Swift 2.0 の Error Handling #yhios

next

Page 34: Swift 2.0 の Error Handling #yhios

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

Page 35: Swift 2.0 の Error Handling #yhios

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

Page 36: Swift 2.0 の Error Handling #yhios

これまでの NSErrorSwift 1.2

Page 37: Swift 2.0 の Error Handling #yhios

これまでの NSErrorSwift 1.2

// エラーチェックを記載しない fm.setAttributes(attr, ofItemAtPath:path, error:nil)

成否に関わらず以下が実行される

Page 38: Swift 2.0 の Error Handling #yhios

Error HandingSwift 2.0

Page 39: Swift 2.0 の Error Handling #yhios

Error HandlingSwift 2.0

// エラーはない ! と明記する try! fm.setAttributes(attr, ofItemAtPath:path)

エラーのときは以下に進まず 強制終了

Page 40: Swift 2.0 の Error Handling #yhios

つまり

Page 41: Swift 2.0 の Error Handling #yhios

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

Page 42: Swift 2.0 の Error Handling #yhios

next

Page 43: Swift 2.0 の Error Handling #yhios

エラーのときの後始末

Page 44: Swift 2.0 の Error Handling #yhios

Error Handlingdo { let handle = try file.open()

try fm.setAttributes(attr, ofItemAtPath:path)

handle.close()

処理が終わったら閉じたい

} catch {

}

でもエラーが発生すると…

ここまでたどり着けない

Page 45: Swift 2.0 の Error Handling #yhios

Error Handlingdo { let handle = try file.open()

defer { handle.close() }

try fm.setAttributes(attr, ofItemAtPath:path)

} catch {

}

最後に処理したいことを先に書く

ここでエラーが発生しても…

ブロックを抜ける直前に処理される

処理が終わったら閉じたい

Page 46: Swift 2.0 の Error Handling #yhios

余談

Page 47: Swift 2.0 の Error Handling #yhios

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

Page 48: Swift 2.0 の Error Handling #yhios

もし try-finally だったとしたら…var stream:Stream? = nil @try { stream = Stream.open(path) fm.setAttributes(attr, ofItemAtPath:path) } @catch let error { } @finally { stream?.close() }

初期化と後始末のスコープ分断を考慮して外側に定義

未初期化を考慮してオプショナルチェイニング

そもそも、どこでエラーが起こるの…?

Page 49: Swift 2.0 の Error Handling #yhios

つまり

Page 50: Swift 2.0 の Error Handling #yhios

こうではなくvar stream:Stream? = nil @try { stream = Stream.open(path) fm.setAttributes(attr, ofItemAtPath:path) } @catch let error { } @finally { stream?.close() }

▶ 入れ物の事前準備が必要 ▶ 3つのブロックに着目 ▶ 流れよりも構文が主役 ▶ どこでエラーになるかが

コードから読めない

Page 51: Swift 2.0 の Error Handling #yhios

こうなるdo { let handle = try file.open()

defer { handle.close() }

try fm.setAttributes(attr, ofItemAtPath:path)

} catch {

}

▶ 事前準備が不要 ▶ 流れが主役

原則成功、ときどき失敗 ▶ コードからエラーが

発生する箇所がわかる

Page 52: Swift 2.0 の Error Handling #yhios

Error Handling は美しいってコト!

Page 53: Swift 2.0 の Error Handling #yhios

next

Page 54: Swift 2.0 の Error Handling #yhios

Swift でエラーを扱う方法

Page 55: Swift 2.0 の Error Handling #yhios

Optional<T>

Page 56: Swift 2.0 の Error Handling #yhios

Optional<T>値があるかないかを扱う型

if let value = optional {

} else { // 値がなかったときにエラーとするかは状況次第 }

▶ 値の有無による判断を強制 ▶ 値がないときがエラーとは限らない

Page 57: Swift 2.0 の Error Handling #yhios

Optional のイメージ

Page 58: Swift 2.0 の Error Handling #yhios

Optional<T>使いどころ

// 例えば、リストの中から値を検索する関数 func find(list, value) -> Index? {

}

▶ 単純に値の有無を提示する ▶ エラーかどうかを決め付けない

Page 59: Swift 2.0 の Error Handling #yhios

いわゆる Either 型

Page 60: Swift 2.0 の Error Handling #yhios

いわゆる Either 型どちらかの状況を表現する型

enum Result<T> {

case Success(T) case Failure(Error) }

▶ 成功値か失敗値かを取る列挙型 ▶ 成否というより状況の切り分けに着目

Page 61: Swift 2.0 の Error Handling #yhios

いわゆる Either 型どちらかの状況を表現する型

switch getResult() {

case .Success(let value):

case .Failure(let error):

}

▶ 戻り値ひとつで状況の切り分けが可能 ▶ 両者を同じ重みで扱う

Page 62: Swift 2.0 の Error Handling #yhios

Either 型のイメージ

Page 63: Swift 2.0 の Error Handling #yhios

いわゆる Either 型使いどころ

▶ 戻り値で二つの場面を提示する ▶ 背反する分岐点を表現する

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

enum Result<T,U> {

case Succeeded<T> case Failed<U> }

switch getResult() {

case .Success(let value):

case .Failure(let error):

}

Page 64: Swift 2.0 の Error Handling #yhios

Fatal Error

Page 65: Swift 2.0 の Error Handling #yhios

Fatal Error致命的なエラー

fatalError("もうムリ…")

▶ 処理が継続できない状況を表現 ▶ 想定外を持ち越さない

以降の処理は実行させない!

Page 66: Swift 2.0 の Error Handling #yhios

Fatal Error のイメージ

Page 67: Swift 2.0 の Error Handling #yhios

Fatal Error使いどころ

▶ Optional で絶対に値が入っているとき ▶ Optional で値が入っていないと困るとき ▶ try が絶対にエラーにならないとき ▶ 処理を継続できないと判断したとき ▶ 相手に責任を取らせたいとき

などなど

活用の場面は幅広い

Page 68: Swift 2.0 の Error Handling #yhios

実際

Page 69: Swift 2.0 の Error Handling #yhios

Fatal ErrorSwift でも積極的に使われている

// 強制アンラップ let value = optional!

// 暗黙アンラップなオプショナル var value:String!

値がなければ fatalError

nil が入っているのに操作したら fatalError

Page 70: Swift 2.0 の Error Handling #yhios

Fatal ErrorSwift でも積極的に使われている

// 強制キャスト let subObj = obj as! SubClass

// エラーを想定しない try! execute()

キャストできなければ fatalError

エラーが起これば fatalError

Page 71: Swift 2.0 の Error Handling #yhios

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

Page 72: Swift 2.0 の Error Handling #yhios

Swift の Error HandlingNew!

Page 73: Swift 2.0 の Error Handling #yhios

エラー型の定義

Page 74: Swift 2.0 の Error Handling #yhios

Error Handling

protocol ErrorType {

}

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

ErrorType プロトコル

Page 75: Swift 2.0 の Error Handling #yhios

Error Handling

enum OpenError : ErrorType {

case NotFound case Readonly case Busy(reason:String) }

▶ エラー型は 列挙型 で表現 ▶ 列挙型を ErrorType に準拠させる ▶ 起こり得るエラーを列記

値付き列挙子も使える

Error 型

Page 76: Swift 2.0 の Error Handling #yhios

関数やメソッドで使う

Page 77: Swift 2.0 の Error Handling #yhios

Error Handling

func open(file:FILE) throws -> Stream { : :

▶ エラーが有り得る機能に throws を付与 ▶ エラーになるかもしれないことが

プログラマーとコンパイラの両者に伝わる

エラーを示唆する

Page 78: Swift 2.0 の Error Handling #yhios

Error Handling

func open(file:FILE) throws -> Stream {

guard _exists(file) else {

throw OpenError.NotFound } : :

▶ エラーは throw で通知する ▶ エラーは列挙子で指定する ▶ throws を指定した機能でだけ通知可能

エラーを通知する

Page 79: Swift 2.0 の Error Handling #yhios

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

Page 80: Swift 2.0 の Error Handling #yhios

Error Handling

do { let stream = try open(file)

} catch { }

▶ 正常処理の範囲を do で表現 ▶ エラーが発生し得る場所に try を明記 ▶ エラーは catch で補足する

エラーに挑む!

エラーを想定する

Page 81: Swift 2.0 の Error Handling #yhios

Error Handling

do { let stream = try open(file)

} catch OpenError.NotFound {

} catch OpenError.ReadOnly {

} catch OpenError.Busy(let reason) {

}

全てのエラーに対処する

Page 82: Swift 2.0 の Error Handling #yhios

Error Handling

do { let stream = try open(file)

} catch OpenError.Busy {

} catch is OpenError {

}

値付き列挙子の値を加味しないことも可能

列挙子を加味せずに捕獲することも可能

全てのエラーに対処する

Page 83: Swift 2.0 の Error Handling #yhios

おさらい

Page 84: Swift 2.0 の Error Handling #yhios

Error Handling

▶ エラーを列挙型で表現起こり得るエラーが一目瞭然

▶ エラーの可能性を throws で示唆プログラマーとコンパイラに意思が伝わる

▶ エラーが起こり得る箇所に try を明記 どこでエラーを想定しているかが明確

▶ エラーを想定したコードが必須強制されると悩まずに済むので楽になる

おさらい

Page 85: Swift 2.0 の Error Handling #yhios

つまり

Page 86: Swift 2.0 の Error Handling #yhios

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

Page 87: Swift 2.0 の Error Handling #yhios

Error Handling のイメージ

Page 88: Swift 2.0 の Error Handling #yhios

Error Handling使いどころ

▶ 達成すべき目的がありそれを達成できない可能性があるとき

▶ 原因が実行時エラーに限られるとき ▶ 原因がいくつか考えられるとき ▶ 原因を提示し、対応を求めたいとき

// 目的が明確で、エラーも有り得る複合的な機能 func open(file:FILE) throws -> Stream

Page 89: Swift 2.0 の Error Handling #yhios

まとめ

Page 90: Swift 2.0 の Error Handling #yhios

エラーを扱う手段

Optional 型▶ 単純に値の有無を提示?いわゆる Either 型▶ 戻り値で分岐点を提示

Fatal Error▶ 強制終了して根本的な改善を迫る

Error Handling▶ 目的を遂行できない時に原因を提示

Page 91: Swift 2.0 の Error Handling #yhios

Swift はこれらの使用を強要する

強要されるとプログラミングが楽になる

Page 92: Swift 2.0 の Error Handling #yhios

つまり

Page 93: Swift 2.0 の Error Handling #yhios

Swift は すごいってコト!

Page 94: Swift 2.0 の Error Handling #yhios

おしまい。

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

Page 95: Swift 2.0 の Error Handling #yhios

おまけ

Page 96: Swift 2.0 の Error Handling #yhios

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

Page 97: Swift 2.0 の Error Handling #yhios

Objective-C からの自動変換

Page 98: Swift 2.0 の Error Handling #yhios

Objective-C からの自動変換

// このような Objective-C コードが - (NSString*)getNameFromPath:(NSString*)path error:(NSError**)error;

// このような Swift コードになる func getNameFromPath(path:String!) throws -> String

▶ 戻り値がクラスの場合 ▶ 最後の引数が NSError** の場合

末尾の NSError を throws に変換

Page 99: Swift 2.0 の Error Handling #yhios

Objective-C からの自動変換

// このような Objective-C コードが - (BOOL)prepareWithOptions:(NSArray*)opts error:(NSError**)error;

// このような Swift コードになる func prepareWithOptions(opts:[AnyObject]!) throws -> Void

▶ 戻り値が BOOL の場合 ▶ 最後の引数が NSError** の場合

末尾の NSError を throws に変換

Page 100: Swift 2.0 の Error Handling #yhios

Objective-C への自動変換

Page 101: Swift 2.0 の Error Handling #yhios

Objective-C への自動変換

// このような Swift コードが func getName(path:String) throws -> String

// このような Objective-C コードになる - (NSString*)getName:(NSString*)path error:(NSError**)error;

▶ 戻り値が @objc 互換オブジェクトの場合 ▶ throws が指定されている場合

throws を NSError に変換

Page 102: Swift 2.0 の Error Handling #yhios

Objective-C への自動変換

// このような Swift コードが func prepare(options:[String]?) throws

// このような Objective-C コードになる - (BOOL)prepare:(NSArray**)options error:(NSError**)error;

▶ 戻り値が Void の場合 ▶ throws が指定されている場合

throws を NSError に変換

Page 103: Swift 2.0 の Error Handling #yhios

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

Page 104: Swift 2.0 の Error Handling #yhios

つまり

Page 105: Swift 2.0 の Error Handling #yhios

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

Page 106: Swift 2.0 の Error Handling #yhios

おしまい。