如何在Swift中使用Result

Result介绍

Swift标准库的Result类型使我们能够使用单个统一类型来表达给定操作的结果(无论成功还是失败)。让我们看一下在哪种情况下Result可能有用的方法,以及一些在开始使用该类型时要牢记的技巧和窍门。

尽管有很多不同的方法可以对Result类型进行建模,但是Swift标准库中内置的方法被声明为通用枚举,它针对结果可能包含的成功值以及遇到的任何错误进行了强类型化。看起来像这样:

enum Result<Success, Failure> where Failure: Error {
   
    case success(Success)
    case failure(Failure)
}

就像上面的声明所示,Result只要Failure类型符合Swift的Error协议,我们就可以用来表示任何成功/失败组合。那么我们如何在实践中使用上述类型,这样做的好处是什么?

例如,让我们看一下URLSession,它是最常用的API之一——使用基于闭包的设计以异步方式返回网络请求的各种结果:

let url = URL(string: "https://www.swiftbysundell.com")!

let task = URLSession.shared.dataTask(with: url) {
   
    data, response, error in
    
    if let error = error {
   
        // Handle error
        ...
    } else if let data = data {
   
        // Handle successful response data
        ...
    }
}

task.resume()

尽管URLSession这些年来已经发展了很多,并且拥有一套功能强大的API,但确切地决定如何处理网络调用的结果有时会有些棘手。因为,如上面的示例所示,dataerror结果一样作为可选参数传递到我们的闭包中-这反过来要求我们在每次进行网络调用时都要解开每个值。

让我们看看使用Result可以帮助我们如何解决该问题。我们将从扩展URLSession新API 入手,该API将一个Result<Data, Error>值传递到其完成处理程序中,而不是一组可选参数。为了实现这一点,我们将对标准API提供给我们的可选选项进行解包(类似于上面的操作),以构造我们的代码Result,如下所示:

extension URLSession {
   
    func dataTask(
        with url: URL,
        handler: @escaping (Result<Data, Error>) -> Void
    ) -> URLSessionDataTask {
   
        dataTask(with: url) {
    data, _, error in
            if let error = error {
   
                handler(.failure(error))
            } else {
   
                handler(.success(data ?? Data()))
            }
        }
    }
}

请注意,通过忽略默认API的URLResponse值(在闭包中使用下划线而不是其参数名称),我们在上面做了一些简化。尽管对于更简单的网络任务,可能不需要检查该响应值,但这并不是我们始终想要做的事情。

如果现在返回到先前的调用站点并对其进行更新以使用新的API,我们可以看到我们的代码变得更加清晰了-因为我们现在可以为successfailure案例编写完全独立的代码路径,如下所示:

let task = URLSession.shared.dataTask(with: url) {
    result in
    switch result {
   
    case .success(let data):
        // Handle successful response data
        ...
    case .failure(let error):
        // Handle error
        ...
    }
}

关于Result上面使用方式的一个有趣的细节是,我们Failure简单地将其类型指定为Error。这意味着任何错误都可以传递到我们的结果中,这反过来又限制了我们在呼叫站点进行更具体的错误处理的选项(因为我们没有任何要处理的潜在错误的详尽列表)。直接与系统API结合使用时,要改变这一点比较棘手,而这又会引发任何错误-当我们构建更具体的抽象形式时,我们通常可以为其设计一个更统一的错误API

例如,假设我们正在构建一个非常简单的图像加载器,可以再次使用来通过网络加载图像URLSession 。但是在开始实际实现加载程序本身之前,让我们首先定义一个枚举,该枚举列出它可能遇到的所有潜在错误。目前,我们只有两种情况——发生了网络错误,或者我们下载的数据被证明是无效的:

enum ImageLoadingError: Error {
   
    case networkFailure(Error)
    case invalidData
}

然后,在构建图像加载器时,我们现在可以专门Result处理上述错误类型-这又使我们能够向呼叫站点发送更多的错误信息:

struct ImageLoader {
   
    typealias Handler = (Result<UIImage, ImageLoadingError>) -> Void

    var session = URLSession.shared

    func loadImage(at url: URL,
                   then handler: @escaping Handler) {
   
        let task = session.dataTask(with: url) {
    result in
            switch result {
   
            case .success(let data):
                if let image = UIImage(data: data) {
   
                    handler(.success(image))
                } else {
   
                    handler(.failure(.invalidData))
                }
            case .
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值