Loading [MathJax]/jax/input/TeX/config.js
前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >了解 Swift 的 Result 类型

了解 Swift 的 Result 类型

作者头像
韦弦zhy
发布于 2020-09-14 03:17:01
发布于 2020-09-14 03:17:01
2.8K00
代码可运行
举报
运行总次数:0
代码可运行

Result

通常希望函数成功返回一些数据,或者如果失败则返回错误。我们通常使用throwing函数对此建模,因为如果函数调用成功,我们将获得数据,但是如果抛出错误,则将运行catch代码块,因此我们可以独立处理这两个函数。但是,如果函数调用没有立即返回怎么办?

我们之前使用URLSession查看了网络代码。现在来看另一个示例,将其添加到默认的SwiftUI模板代码中:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
Text("Hello, World!")
    .onAppear {
        let url = URL(string: "https://www.apple.com")!
        URLSession.shared.dataTask(with: url) { data, response, error in
            if data != nil {
                print("We got data!")
            } else if let error = error {
                print(error.localizedDescription)
            }
        }.resume()
    }

加载文本视图后,网络请求将立即开始,从 apple.com 提取一些数据,并根据网络请求是否起作用打印两个消息之一。

如果您还记得的话,我说完成闭包将把dataerror设置为一个值——不能两者皆有,也不能两者都没有,因为这两种情况不会一起出现。但是,由于URLSession对我们没有强制执行此约束,因此我们需要编写代码来处理不可能的情况,只是要确保覆盖所有情况。

Swift为解决这种混乱提供了解决方案,它是一种称为Result的特殊数据类型。这为我们提供了所需的行为,同时还可以与非阻塞函数配合使用,这些函数是异步执行工作的,因此它们不会阻塞主代码的运行。另外,它还使我们可以返回特定类型的错误,从而更容易知道出了什么问题。

一开始可能感觉语法有点奇怪,这就是为什么我要缓慢地给您热身的原因——这个东西确实很有用,但是如果您深入一探,可能会感觉就像倒退了一步。

我们要做的是为上述网络代码创建一个包装器,以便它使用 Swift 的Result类型,这意味着您可以清楚地看到前后。

首先,我们需要定义可以引发哪些错误。您可以定义任意多个,但在这里我们将说 URL 错误,请求失败或发生未知错误。将此枚举放在ContentView结构体之外:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
enum NetworkError: Error {
    case badURL, requestFailed, unknown
}

接下来,我们将编写一个返回Result的方法。请记住,Result是为了表示某种成功或失败而设计的,在这种情况下,我们要说的是,成功案例将包含从网络返回的任何内容的字符串,而错误将是某种NetworkError

我们将四次编写相同的方法,但是会增加复杂性,因此您可以了解到底该如何使用。首先,我们将立即发送一个badURL错误,这意味着将此方法添加到ContentView中:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
func fetchData(from urlString: String) -> Result<String, NetworkError> {
    .failure(.badURL)
}

如您所见,该方法的返回类型为Result <String,NetworkError>,表示成功时为字符串,失败时为NetworkError值。尽管非常快,但这仍然是一个阻塞函数调用。

我们真正想要的是一个非阻塞调用,这意味着我们无法将Result作为返回值发送回去。取而代之的是,我们需要使我们的方法接受两个参数:一个用于要获取的URL,另一个是将用值调用的完成闭包。这意味着该方法本身不返回任何内容。它的数据通过完成关闭传递回去,将来会在某个时候调用。

同样,我们将使此返回.badURL错误,以使事情变得简单。代码如下:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
func fetchData(from urlString: String, completion: (Result<String, NetworkError>) -> Void) {
    completion(.failure(.badURL))
}

现在,我们有一个完成闭包的原因是我们现在可以使该方法成为非阻塞的:我们可以开始一些异步工作,使方法返回,以便其余代码可以继续,然后在稍后的任何时候调用完成闭包。

这里有一个很小的复杂性,尽管我之前已经简短地提到了它,但它变得很重要。当我们将闭包传递给函数时,Swift需要知道是立即使用它还是以后使用它。如果立即使用默认值——那么Swift很乐意运行闭包。但是,如果稍后使用它,则可能创建的闭包已被销毁并且不再存在于内存中,在这种情况下,闭包也将被销毁并且无法再运行。

为了解决这个问题,Swift让我们将闭包参数标记为@escaping,这意味着:

对于我们的方法,我们将运行一些异步工作,然后在完成后调用闭包。这可能立即发生,也可能需要几分钟。我们不在乎。关键是方法返回后,闭包仍需要保留,这意味着我们需要将其标记为@escaping。如果您担心忘记这一点,没有必要:Swift始终会拒绝构建代码,除非您添加@escaping属性。

这是我们函数的第三个版本,它使用@escaping作为闭包,因此我们可以异步调用它:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
func fetchData(from urlString: String, completion: @escaping (Result<String, NetworkError>) -> Void) {
    DispatchQueue.main.async {
        completion(.failure(.badURL))
    }
}

请记住,可以在将来的任何时候调用完成闭包,并且仍然可以正常使用。

现在,对于该方法的第四个版本,我们将把Result代码与之前的URLSession代码混合。这将具有完全相同的函数签名——接受字符串和闭包,但不返回任何内容——但现在我们将以不同的方式调用完成闭包:

  1. 如果网址不正确,我们将调用 completion(.failure(.badURL))
  2. 如果我们从请求中获得有效数据,则将其转换为字符串,然后调用 completion(.success(stringData))
  3. 如果我们从请求中返回错误,我们将调用 completion(.failure(.requestFailed))
  4. 如果我们以某种方式无法获取数据或出现错误,则我们将调用 completion(.failure(.unknown))

唯一的新事物是如何将Data实例转换为字符串。如果您还记得的话,以前使用过 let data = Data(someString.utf8) ,当从Data转换为String时,代码有些相似:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
let stringData = String(decoding: data, as: UTF8.self)

好的,现在是我们第四遍方法的时候了:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
func fetchData(from urlString: String, completion: @escaping (Result<String, NetworkError>) -> Void) {
    // 检查URL是否正常,否则返回失败
    guard let url = URL(string: urlString) else {
        completion(.failure(.badURL))
        return
    }

    URLSession.shared.dataTask(with: url) { data, response, error in
        // 任务已完成–将工作移动到主线程
        DispatchQueue.main.async {
            if let data = data {
                // 成功:将数据转换为字符串并返回
                let stringData = String(decoding: data, as: UTF8.self)
                completion(.success(stringData))
            } else if error != nil {
                // 任何形式的网络故障
                completion(.failure(.requestFailed))
            } else {
                // 这个应该不可能发生,但我们在这里写一下
                completion(.failure(.unknown))
            }
        }
    }.resume()
}

我知道它花了很多时间,但是我想一步一步地解释它,因为有很多需要接受的东西。它为我们提供了更加简洁的API,因为我们现在可以始终确保我们可以得到一个字符串或错误——无法同时获得它们或两者都不是,因为那不是Result的工作原理。更好的是,如果确实收到错误,则它一定是NetworkError中指定的情况之一,这使错误处理变得容易得多。

到目前为止,我们所做的只是编写使用Result的函数;我们还没有编写任何能处理返回结果的文件。请记住,无论发生什么情况,结果始终包含两条信息:结果的类型(成功或失败)以及其中的某些内容。对我们来说,可以是字符串,也可以是NetworkError。\

在幕后,Result实际上是一个具有关联值的枚举,Swift具有非常特殊的语法来处理这些值:我们可以打开Result,并编写诸如case .success(let str)之类的情况表示“如果这是成功后,将字符串里面的内容赋值一个名为str的新常量。

看到所有这些都比较容易,因此让我们将新方法附加到onAppear闭包中,并处理所有可能的情况:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
Text("Hello, World!")
    .onAppear {
        self.fetchData(from: "https://www.apple.com") { result in
            switch result {
            case .success(let str):
                print(str)
            case .failure(let error):
                switch error {
                case .badURL:
                    print("Bad URL")
                case .requestFailed:
                    print("Network problems")
                case .unknown:
                    print("Unknown error")
                }
            }
        }
    }

希望现在你能看到好处:我们不仅消除了检查返回的内容的不确定性,还完全消除了可选值。甚至连错误处理的默认情况都不需要了,因为所有可能的NetworkError情况都被覆盖了。

译自 Understanding Swift’s Result type

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
Kanna 与 Swift:结合使用提升网络请求效率
Kanna 是一个基于 Swift 的轻量级、高性能的 XML/HTML 解析库,它能够帮助开发者快速解析和处理网络返回的 HTML 或 XML 数据。通过结合 Kanna 和 Swift 的网络请求功能,我们可以构建更加高效、灵活的网络交互模块。本文将详细介绍如何在 Swift 中使用 Kanna 提升网络请求的效率,并通过实际代码示例展示其强大的功能。
小白学大数据
2025/03/06
1220
Swift 3到5.1新特性整理
Swift 5.0 最重要的自然是ABI Stability, 对此可以看这篇 Swift ABI 稳定对我们到底意味着什么 。
小刀c
2022/08/16
4.8K0
Swift 3到5.1新特性整理
Swift基础语法(四)
在Swift5之前,我们一般是采用上面的方式来处理异常,在Swift5之后,苹果推出了一个Result枚举,Result枚举可以更加优雅地去处理异常。
拉维
2020/07/06
4.1K0
Swift基础语法(四)
手把手带你撸一个网易云音乐首页(二)
Hello,大家好,转眼已经来到了7月份,记得鲁迅说过:不管你上半年混的有多惨,请不要气馁,因为伟大的事业都是在下半年完成的。
HelloWorld杰少
2022/08/04
1.4K0
手把手带你撸一个网易云音乐首页(二)
Swift 中的 MainActor 使用和主线程调度
MainActor 是Swift 5.5中引入的一个新属性,它是一个全局 actor,提供一个在主线程上执行任务的执行器。在构建应用程序时,在主线程上执行UI更新任务是很重要的,在使用几个后台线程时,这有时会很有挑战性。使用@MainActor属性将帮助你确保你的UI总是在主线程上更新。
韦弦zhy
2022/11/14
3.6K0
Swift 中的 MainActor 使用和主线程调度
Swift 5.5 新特性
SE-0296提案终于为开发者带来了期待已久的 async/await,语法基本上和javascript中的很像。
小刀c
2022/08/16
2.7K0
Swift 5.5 新特性
iOS开发之网络代码进化史
目前,iOS 原生网络请求数据共有 4 种方式,分别是传统 Completion 回调、Completion 回调 + Result、Combine 框架与Swift Concurrency (async/await)。本文以下载图片为例,详细讲解 4 种网络请求的差异。
YungFan
2025/03/11
1010
戴铭的 Swift 小册子
越来越多同学打算开始用 Swift 来开发了,可很多人以前都没接触过 Swift。这篇和我以前文章不同的是,本篇只是面向 Swift 零基础的同学,内容主要是一些直接可用的小例子,例子可以直接在工程中用或自己调试着看。
Swift社区
2021/12/06
2.3K0
戴铭的 Swift 小册子
Swift-MVVM 简单演练(一)
Swift-MVVM 简单演练(二) Swift-MVVM 简单演练(三) Swift-MVVM 简单演练(四) 前言 最近在学习swift和MVVM架构模式,目的只是将自己的学习笔记记录下来,方便自己日后查找,仅此而已!!! 本来打算一篇全部搞定的,但是简书每篇文章只能写大约不超过15000字的内容,因此只能分开写了。 如果有任何问题,欢迎和我一起讨论。当然如果有什么存在的问题,欢迎批评指正,我会积极改造的! ---- 这篇文章都写啥 自定义NavgationBar 抽取便利构造函数 初步的下拉刷新/上
用户1890628
2018/05/10
10.7K0
通过 3 个简单的步骤测试使用了系统单例的 Swift 代码
大多数为苹果的任何平台编写的应用程序都依赖基于单例的API。从UIScreen到UIApplication再到NSBundle,静态API在Foundation、UIKit和AppKit中无处不在。
韦弦zhy
2022/03/30
4910
Swift 中的 async/await
async-await 是在 WWDC 2021 期间的 Swift 5.5 中的结构化并发变化的一部分。Swift中的并发性意味着允许多段代码同时运行。这是一个非常简化的描述,但它应该让你知道 Swift 中的并发性对你的应用程序的性能是多么重要。有了新的 async 方法和 await 语句,我们可以定义方法来进行异步工作。
韦弦zhy
2022/11/11
3.7K0
Swift 中的 async/await
Swift 5.7 新特性
Swift 5.7 内置于 Xcode 14,重点增加了如下几个与实际开发相关的新特性。
YungFan
2022/06/17
9790
Swift网络爬虫与数据可视化的结合
前言 在当今数字化时代,数据的重要性不言而喻。Swift,作为一种现代的编程语言,以其高性能、易用性和安全性,成为了开发iOS和macOS应用的首选。本文将探讨如何使用Swift来开发一个网络爬虫,以及如何将爬取的数据进行可视化展示。
小白学大数据
2024/07/15
1730
Swift 中的 Task
Swift 中的 Task 是 WWDC 2021 引入的并发框架的一部分。任务允许我们从非并发方法创建并发环境,使用 async/await 调用方法。
韦弦zhy
2022/11/11
3.6K0
Swift 中的 Task
Swift 中的 AsyncThrowingStream 和 AsyncStream
AsyncThrowingStream 和 AsyncStream是Swift 5.5中由SE-314引入的并发框架的一部分。异步流允许你替换基于闭包或 Combine 发布器的现有代码。
韦弦zhy
2022/11/14
1.5K0
Swift基础 嵌套
翻译自:https://docs.swift.org/swift-book/LanguageGuide/Closures.html#ID102
郭顺发
2023/07/17
2980
Swift 项目中涉及到 JSONDecoder,网络请求,泛型协议式编程的一些记录和想法
最近项目开发一直在使用 swift,因为 HTN 项目最近会有另外一位同事加入,所以打算对最近涉及到的一些技术和自己的一些想法做个记录,同时也能够方便同事熟悉代码。
用户7451029
2020/06/16
6.9K0
iOS 面试策略之系统框架-网络、推送与数据处理
如果说移动时代的前身是什么,我想一个可能的答案就是网络时代。网络的兴起,让所有设备相连成为了可能,也催生了电商、社交、搜索等多个领域的商业巨头。而移动时代,则是网络时代的必然延伸,它代表着更便捷、更广阔、更深入的连接。
会写bug的程序员
2021/05/15
1.9K0
iOS 面试策略之系统框架-网络、推送与数据处理
Swift 5.2到5.4新特性整理
SE-0287提案改进了Swift使用隐式成员表达式的能力。Swift 5.4之后不但可以使用单个 使用,而且可以链起来使用。
小刀c
2022/08/16
2.3K0
Swift 5.2到5.4新特性整理
结构化并发
async/await是一种编写自然且高效异步代码的语言机制。异步函数(使用async声明)在执行任何挂起点的地方(使用await标记)都可以放弃它所在的线程,这对构建高并发系统非常有必要。
DerekYuYi
2022/01/12
3.1K0
相关推荐
Kanna 与 Swift:结合使用提升网络请求效率
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
本文部分代码块支持一键运行,欢迎体验
本文部分代码块支持一键运行,欢迎体验