NSExceptionをSwiftでハンドリングする...のは分かったけど、どうしてそうしなきゃならないか
あらすじ
Objective-Cのクラスが絡むとあるエラーを直したくて、まずはExceptionが発生するテストをSwiftで書こうと思った。Objective-Cのクラスの内部ではNSException由来のExceptionが起こるようになっていて、XCTAssertThrows()
を使えばテストが書けると思ったが、うまくいかなかった。
try { ~ } catch let error { ~ }
とか使ってもダメ。
どうやらNSExceptionをSwiftでハンドリングすることはできなくて、ちょっとした工夫をしてやる必要があるらしい。Objective-Cの世界で例外をキャッチしてやるということ。なのでこんな感じのヘルパーを作ってあげると良い。
typedef void (^VoidBlock)(void);
BOOL throwsToBool(VoidBlock const block) {
@try {
block();
}
@catch (NSException * const notUsed) {
return YES;
}
return NO;
}
このコード自体はXCTest: The Good Parts を参考にした。
使い方としては、
class FooTest: XCTestCase {
func testCase() throws {
XCTAssertTrue(throwsToBool {
ObjectiveCWorld.dangerMethod()
}, "dangerMethod() 呼んだらException!")
}
}
って感じ。
でもどうしてObjective-Cの世界でのNSExceptionがハンドリングできないのか。がいまいちわからないので調べたい。
NSErrorとNSExceptionについて整理する
When you’re writing code with Objective-C, exceptions are used solely for programmer errors, like out-of-bounds array access or invalid method arguments. These are the problems that you should find and fix during testing before you ship your app.
All other errors are represented by instances of the NSError class.
Dealing with Errors
とあるので...。プログラマが悪くて出荷する前に認識しておくべきようなエラーはExceptionで、そのほかはNSErrorで表現されると書いてある。
Handling Cocoa Errors in Swift | Apple Developer Documentationにも。
(前略) However, there’s no safe way to recover from Objective-C exceptions in Swift. To handle Objective-C exceptions, write Objective-C code that catches exceptions before they reach any Swift code.
ふーむなるほどー。もうそういうものという感じか