內容目錄
Error: Basic
// (1) Create a enum for 'Error'
enum AuthorizationError: Error {
case accountNotExisted
case accountBlocked(reason: String)
case needTwoFactor
}
// (2) somewhere to 'throws' the error
func accountHandler(existed: Bool, status: String, need2Factor: Bool) throws -> Bool {
guard existed else {
throw AuthorizationError.accountNotExisted
}
guard status == "active" else {
throw AuthorizationError.accountBlocked(reason: "be_reported_by_someone")
}
guard !need2Factor else {
throw AuthorizationError.needTwoFactor
}
return true
}
// (3) 'do' the function and 'try' to 'catch' the error
func accountVerifyTest() {
do {
let _ = try accountHandler(existed: true, status: "blocked", need2Factor: false)
} catch {
print("Something wrong: \(error)")
}
}
Error: Async / Closure
func accountVerifyApi(completion: @escaping (() throws -> Bool) -> Void) {
// simulate a networking service api
DispatchQueue.main.asyncAfter(deadline: .now() + 1.0) {
do {
let result = try accountHandler(existed: true, status: "blocked", need2Factor: false)
completion { return result }
} catch {
completion { throw error }
}
}
}
accountVerifyApi { (result) in
do {
let res = try result()
print(res)
} catch {
print(error)
}
}
Error => NSError
Error
相對 NSError
較為開放,但如果要將Error Message也封裝在Error的宣告範圍裡,就沒有像NSError那麼靈活,但!Swift 3之後,加了Error與NSError的完美Bridging,參考以下範例囉~
// 想為Error加上額外資訊
// 方法ㄧ: 使用extension 擴充方法
extension AuthorizationError {
var getDetail: String {
switch self {
case .accountNotExisted: return "account not existed"
case .accountBlocked(reason: let reason): return reason
case .needTwoFactor: return "need two factor"
}
}
}
// 看起來很單純,但無法確保 mapping 可以如預期
let aerror = error as! AuthorizationError
print(aerror.getDetail)
// 方法二: 使用NSError Bridging
// (1) LocalizedError
extension AuthorizationError: LocalizedError {
var errorDescription: String? {
switch self {
case .accountNotExisted: return "account not existed"
case .accountBlocked(reason: let reason): return reason
case .needTwoFactor: return "need two factor"
}
}
}
// (2) CustomNSError
extension AuthorizationError: CustomNSError {
public static var errorDomain: String {
return "AuthorizationError"
}
public var errorCode: Int {
switch self {
case .accountNotExisted: return 1
case .accountBlocked(reason: _): return 2
case .needTwoFactor: return 3
}
}
public var errorUserInfo: [String : Any] {
switch self {
case .accountNotExisted:
return [ "message": "account not existed"]
case .accountBlocked(reason: let reason):
return [ "message": reason]
case .needTwoFactor: return [ "message": "need two factor"]
}
}
}
// 第二種較複雜,但可以準確橋接到NSError
let nserror = error as NSError
print(nserror.localizedDescription) // LocalizedError
print(nserror.userInfo) // CustomNSError
throws vs rethrows
// (1) 使用 throws -> [T] 時
public func doubleElementArray<T>(_ transform: () throws -> T) throws -> [T] {
let result = try transform()
return [result, result]
}
// "必須"在呼叫函式前面加上 'try',否則compile不會過
let result = try doubleElementArray { () -> String in
return "Peace"
}
// 但並非每個狀況都會遇到Exception,特別是使用泛型時
// (2) 因此可以改用 rethrows -> [T]
public func doubleArray<T>(_ transform: () throws -> T) rethrows -> [T] {
let result = try transform()
return [result, result]
}
// 可行
let doubleString = doubleArray { () -> String in
return "Peace"
}
// 可行
let doubleInt = try doubleArray { () -> Int in
throw DoubleArrayError.forceIntThrow
}
rethrows = return or throws,這樣看rethrows也許比較好理解,再配合Swift .map
的源碼:
func map<T>(_ transform: (Element) throws -> T) rethrows -> [T]
再問問自己,過去用.map
時,有多少次用到try
?很少吧!沒錯這就是rethrows
默默付出的結果!