Swift 使用 Combine 进行开发 从入门到精通七

Combine 系列

  1. Swift Combine 从入门到精通一
  2. Swift Combine 发布者订阅者操作者 从入门到精通二
  3. Swift Combine 管道 从入门到精通三
  4. Swift Combine 发布者publisher的生命周期 从入门到精通四
  5. Swift Combine 操作符operations和Subjects发布者的生命周期 从入门到精通五
  6. Swift Combine 订阅者Subscriber的生命周期 从入门到精通六
    在这里插入图片描述

1 . 使用 Combine 进行开发

通常从利用现有的发布者、操作符和订阅者来组成管道开始。 本书中的许多示例突出了各种模式,其中许多模式旨在对界面内的用户输入提供声明性响应。

你可能还希望创建更容易集成到 Combine 的 API。 例如,创建一个封装远程 API 的发布者,返回单个结果或一系列结果。 或者,你可能正在创建一个订阅者来随着时间的推移去处理和消费数据。

2. 关于管道运用的思考

在用 Combine 进行开发时,有两种更广泛的发布者模式经常出现:期望发布者返回单一的值并完成,和期望发布者随着时间的推移返回多个值。

我把第一个称作 “one-shot”(一次性)的发布者或管道。 这些发布者会创建单个响应(或者可能没有响应),然后正常终止。

我把第二个称作 “continuous”(连续)的发布者。 这些发布者和相关管道应始终处于活动状态,并提供处理持续事件的方法。 在这种情况下,管道的寿命要长得多,而且通常不希望此类管道发生失败或终止。

当你在考虑如何使用 Combine 进行开发时,把管道视作这两个类型之一,并把它们混合在一起以实现你的目标,往往是很有帮助的。 例如,模式 使用 flatMap 和 catch 在不取消管道的情况下处理错误 明确地在不间断的管道中使用一次性的管道来处理错误。

当你创建发布者或管道的实例时,好好思考你希望它如何工作是值得的 —— 要么是一次性的,要么是连续的。 你的选择将关系到你如何处理错误,或者你是否要处理操纵事件时序的操作符 (例如 debounce 或者 throttle).

除了管道或发布者将提供多少数据外,你还经常需要考虑管道将提供哪种类型对。 许多管道更多的是通过各种类型转换数据,并处理该过程中可能出现的错误情况。 该情况的一个例子是返回一个管道,在管道中如例子 通过用户输入更新声明式 UI 所示返回一个列表,以提供一种表示“空”结果的方法,即使列表中永远不会有超过 1 个元素。

最终,使用 Combine 来连接两端的数据:当数据可用时,由原始的发布者发送它们,然后订阅者最终消费数据。

3. Combine 发布者和订阅者涉及到的 Swift 类型

当你在 Swift 中构建管道时,函数链导致该类型被聚合为嵌套的通用类型。 如果你正在创建一个管道,然后想要将该管道作为 API 提供给代码的另一部分,则对于开发人员来说,暴露的属性或函数的类型定义可能异常复杂且毫无用处。

为了说明暴露的类型复杂性,如果你从 PassthroughSubject 创建了一个发布者,例如:

let x = PassthroughSubject<String, Never>()
    .flatMap { name in
        return Future<String, Error> { promise in
            promise(.success(""))
            }.catch { _ in
                Just("No user found")
            }.map { result in
                return "\(result) foo"
        }
}

结果的类型是:

Publishers.FlatMap<Publishers.Map<Publishers.Catch<Future<String, Error>, Just<String>>, String>, PassthroughSubject<String, Never>>

当你想要暴露这个 subject 时,所有这些混合的细节可能会让你感到非常迷惑,使你的代码更难使用。

为了清理该接口,并提供一个好用的 API,可以使用类型擦除类来包装发布者或订阅者。 这样明确隐藏了 Swift 中从链式函数中构建的类型复杂性。

用于为订阅者和发布者暴露简化类型的两个类是:

每个发布者还继承了一种便利的方法 eraseToAnyPublisher(),它返回一个 AnyPublisher 实例。 eraseToAnyPublisher() 的使用非常像操作符,通常作为链式管道中的最后一个元素,以简化返回的类型。

如果你在上述代码的管道末尾添加 .eraseToAnyPublisher()

let x = PassthroughSubject<String, Never>()
    .flatMap { name in
        return Future<String, Error> { promise in
            promise(.success(""))
            }.catch { _ in
                Just("No user found")
            }.map { result in
                return "\(result) foo"
        }
}.eraseToAnyPublisher()

结果的类型将被简化为:

AnyPublisher<String, Never>

同样的技术在闭包内构造较小的管道时将非常有用。 例如,当你想在闭包中给操作符 flatMap 返回一个发布者时,你可以通过明确的声明闭包应返回 AnyPublisher 来获得更简单的类型推断。 可以在模式 有序的异步操作 中找到这样的一个例子。

参考

https://heckj.github.io/swiftui-notes/index_zh-CN.html

代码

https://github.com/heckj/swiftui-notes

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值