引入
在 iOS 开发中,经常会有很多时候接收异步回调,比如网络部分 URLSession
的 dataTask(with:completionHandler:)
方法,经常会有如下的代码出现:
let request = URLRequest(url: URL(string: "https://xxxx")!)
URLSession.shared.dataTask(with: request) {
data, response, error in
if error != nil {
//处理错误error
} else {
//处理数据data
}
}
这里有三个参数:(Data?, URLResponse?, Error?)
,它们都是可选型,当请求成功时,Data
参数包含 response
中的数据,Error
为 nil
;当发生错误时,Error
指明具体的错误,Data
为 nil
。显然 data
和 error
是互斥的,不存在 data
和 error
同时为 nil
或者同时非 nil
的情况,但是编译器却无法确认这个事实。于是在 Swift 5 中,新增了一个枚举类型 Result
,使我们能够更简单、更清晰地处理复杂代码中的错误。
定义
public enum Result<Success, Failure> where Failure: Error {
/// A success, storing a `Success` value.
case success(Success)
/// A failure, storing a `Failure` value.
case failure(Failure)
...
}
- 接收两个泛型参数,一个为
Success
,一个为Failure
,但是Failure
必须是Error
类型的 -
Success
代表正确执行的值 -
Failure
代表出现问题时的错误值
简单案例
改写异常章节中的案例,用Result
来处理,比较与之前案例的区别
import UIKit
// 定义Error
enum FileReadError: Error {
case FileISNull
case FileNotFound
}
// 用Result处理
func readFileContent(filePath: String) -> Result<String, FileReadError> {
// 1.filePath为""
if filePath == "" {
return .failure(.FileISNull)
}
// 2.filepath有值,但是没有对应的文件
if filePath != "/User/Desktop/123.plist" {
return .failure(.FileNotFound)
}
// 3.取出其中的内容
return .success("123")
}
//let result = readFileContent(filePath: "") //文件为空
//let result = readFileContent(filePath: "11111") //文件找不到
let result = readFileContent(filePath: "/User/Desktop/123.plist") //123
// 处理Result
switch result {
case .failure(let error):
switch error {
case .FileISNull:
print("文件为空")
case .FileNotFound:
print("文件找不到")
}
case .success(let content):
print(content)
}
错误处理
有没有发现,有了Result
,处理异常有了明显的变化,显得更简洁
// swift5 之前的 throw 的异常需要 do try catch 处理
do {
handle(try String(contentsOfFile: myFile))
} catch {
handleError(error)
}
// 有了Reuslt之后
let str = Result { try String(contentsOfFile: myFile) }
handle(str)
异步失败处理案例
截止目前,URLSession
的dataTask
尚未提供返回Result
的方法,所以还不能改写前面的案例,只能举个简单的异步回调的案例(实际开发中不会这样处理,仅仅学习演示之用)
// 1.定义Error
enum NetworkError: Error {
case URLInvalid
}
// 定义一个函数,包含一个逃逸闭包进行异步回调
func getInfo(from urlString: String, completionHandler: @escaping (Result<String, NetworkError>) -> Void) {
if urlString.hasPrefix("https://") {
// 经过一系列网络处理以后得到一个服务器返回的数据
let data = "response result"
// 获取数据
completionHandler(.success(data))
}
else{
// URL有问题
completionHandler(.failure(.URLInvalid))
}
}
// 调用函数
getInfo(from: "https://www.baidu.com") { result in
// 处理Result
switch result {
case .success(let content):
print(content)
case .failure:
// 如果参数不是https://开头 会打印
print("url有问题")
}
}
优点
- 使用
Result
类型处理异步失败变得更加自然优雅 - 处理
Result
时很简单,要么得到错误,要么得到正确的值 - 限制返回的错误类型,这个错误也是一个枚举
网友评论