当我们使用SwiftUI进行MVVM模式开发的后期,将越来越依赖使用Combine框架,苹果公司对于Combine是这样描述的:
Combine框架提供了一个声明性的Swift API,用于随着时间的推移处理值。这些值可以表示多种异步事件。组合声明发布者以公开可以随时间变化的值,并结合订阅者从发布者那里接收这些值。
这样的描述很官方,读很多次依然不知道Combine框架是做什么的,其实我们可以通俗的理解为对输入、输出数据流的处理(个人理解为:流水线模式数据处理)。本文将对Combine框架的使用场景,理论基础进行一个简单的介绍。
Combine框架使用一种叫函数响应式的编程方式,“函数响应式编程(Functional reactive programming)”简写为FRP。其处理流程为:收到网络请求 -> 映射到JSON模式 -> 发布映射成功的数据 -> 数据变化导致View重建(UI显示接收到的数据)
而Combine框架我们需要关注3个重点:发布者 Publisher和特殊发布者Subjects -> Operator操作者(介于发布者与订阅者之间) -> Subscriber订阅者,了解Combine的流程与概念后我们只需要重点学习Operator的相关处理函数即可。
下面我们将使用Combine框架完成了一个点击开始计数的代码示例(为了方便拷贝代码,我将ViewModel与View放到了一个文件里):
import SwiftUI
import Combine //导入Combine框架
//ViewMode 视频模型
class TimerCount:ObservableObject{
@Published var counter = 0 //发布对象
var subscription:AnyCancellable? //定义一个可取消的订阅者 Subscriber
func startTimer(){
print("计数开始")
//subscription 订阅 Timer的发布
subscription = Timer //Foundation 自带的时间发布者
//every发布频率:每0.1秒,ON在:主线程,in:模式:一种包含一个或多个其他运行循环模式的伪模式。
.publish(every: 0.1, on: .main, in: .common)
//-------------下面是操作者Operator--------------------//
.autoconnect()//自动连接到上游可连接发布程序的发布程序。
.scan(0, { count,_ in
return count + 1 //通过向闭包提供当前元素以及该闭包返回的最后一个值,从上游发布器转换元素。
})
.sink(receiveCompletion: { _ in
print("发布完成")
}, receiveValue: { count in
print("同步更新UI数据")
self.counter = count
})
}
func stopTimer(){
print("停止计数")
subscription?.cancel()//取消订阅
}
//返回一个随机的颜色
func randomPlaceholderColor() -> Color{
[.red,.black,.orange,.mint,.purple,.yellow,.gray,.green,.black,.brown,.pink,.primary].randomElement()!
}
}
//View 视图
struct TimerCountView: View {
@StateObject var timer = TimerCount()//订阅者
var body: some View {
VStack(spacing:35){
Text("\(timer.counter)")
.font(.largeTitle)
.fontWeight(.bold)
.foregroundColor(timer.randomPlaceholderColor())//随机颜色
HStack{
myButton("开始计数"){ timer.startTimer() }
myButton("停止计数"){ timer.stopTimer() }
}
}
}
func myButton(_ text:String, action: @escaping () -> Void) -> some View{
Button(action: action){ Text(text) }
.buttonStyle(.borderedProminent)
}
}
在这篇文章里有对 发布者、订阅者进行一个简单的说明:http://www.55mx.com/ios/159.html
通过上面的演示代码我们可以看到,subscription 是一个订阅者,通过 “ subscription = Timer ”开始对发布者“Timer”发出订阅请求,然后通过后续的 .publish 、.autoconnect 、.scan 等操作者对数据处理后,由.sink 完成订阅,这就是官方文档所说的”时间的推移“从上到下分步处理后交给下一级继续处理。我们这里重点理解每个操作者函数的使用方式和合适的使用时间、位置,就基本就算得上Combine入门了。
上面的代码简单理解为:创建一个每0.1秒发布一次的”逻辑规则“,通过.scan来观察1个或者多个元素的变化、处理(可能会失败的)异步操作、随时间改变流的内容。处理事件流、时间、错误的发生,并协调系统如何去响应一个事件,这就是是函数响应式编程的核心。
要学习Combine我个人觉得应该从正确的将这个单词翻译成恰当的中文,通常我们使用翻译软件翻译成“组合”,我觉得将其翻译成“数据组装器”更为合适,因为从: 发布者(Publisher) -> 操作者(Operators) -> 订阅者(Subscriber) 数据流向过程可以看出来,这就像工厂的流水线一样。而这条流水线将是我们要理解的“管道”。发布者将数据放到流水线上(管道),操作者负责对这些数据进行处理,如“过滤、合并、解码”等操作。最终将处理好的数据传输给订阅者。这就是整个Combine框架的核心工作。
(Combine 流水作业示意图)
从 Publisher 到 Operator 再到 Subscriber 的调用是链式的,Operator 的输入是 Publisher,输出仍然是 Publisher,得益于 Operator 的高度解耦,我们可以对数据进行多次处理,并且对其的测试也是比较容易的。
publisher 和 subscriber,在 Swift 中被描述为协议。 从上面的示意图可以看到,不管是发布者、订阅者还是操作者,他们总是持有相同的2个数据,发布者的Output输出的类型与订阅者Input输入的类型是同一个数据,只是经过操作者的时候会对数据进行处理。而Failure携带的是每次数据可能会产生的错误信息。我们可以使用Never忽略掉错误。
在知道Combine的核心工作之后,在开发使用时需要对下面的几个核心概念进行深入的理解。这也是数据流向及处理的核心。
我们根据上面的Combine 流水作业示意图顺序,逐步介绍每个阶段的数据处理函数。
1、Publisher(发布者)
发布者提供一个可获取(网络抓取、读取本地文件等)、可生成(自定义内容)的数据,也是将数据放到流水线(管道)上让操作者处理的源头,如上面的代码中我们通过Foundation框架的Timer发布一个定时器(自定义生成的内容)。如果发布者没有被订阅(“ subscription = Timer ”),则Timer不会发布任何的数据。
当我们使用.scan对发布数据处理时,会用两种 相关的类型(associatedtype)来描述:Output(发布的对象)、Failure(发布失败),比如上面代码里我们发布的对象是一个Int值,使用”_“忽略了失败结果。
发布者如果返回 String类型的数据 ,并且可能返回的错误类型是URLError,那么发布者可以用 <String, URLError> 来描述。
最新的发布者请参考官方文档:https://developer.apple.com/documentation/combine/publisher
发布者是Combine的重要核心功能之一,提供了若干且官方会不断更新其提供的发布者函数,我们需要深刻学习发布者提供的功能,所以我使用单独的文章对发布者的功能整理,请稳步下面的地址:
发布者详细介绍与说明:http://www.55mx.com/ios/184.html
2、Operators(操作者)
操作者是苹果参考文档中包含的一些预构建功能的便捷名称。 操作者用来组合成管道(流水线上处理各部份的工厂)。 许多操作者会接受开发人员的一个或多个闭包,以定义业务逻辑,同时保持并持有发布者/订阅者的生命周期(让流水线正常运行)。
一些操作者支持合并来自不同管道的输出(多条流水线上的数据合并)、更改数据的时序或过滤所提供的数据。 操作者可能还会对操作类型有限制(不符合的数据抛弃、替换、修改等), 还可用于定义错误处理和重试逻辑、缓冲和预先载入以及支持调试。
所以操作者是一个既像订阅者又像发布者的对象。操作者是同时实现了订阅者协议和发布者协议的类。 它们支持订阅发布者,并将结果发送给任何订阅者。
最新的操作者请参考官方文档:https://developer.apple.com/documentation/combine/future-publisher-operators
操作者详细介绍与说明:http://www.55mx.com/ios/185.html
3、 Subscriber(订阅者)
相比发布者,订阅者学习起来比较容易,Combine 里有两个内建的订阅者: Assign 和 Sink。 而我们使用的SwiftUI 中有一个订阅者: onReceive。所以,只要掌握了这3个功能就基本能就应复开发中的大部份问题。
订阅者支持取消操作,取消时将终止订阅关系以及所有流完成之前,由发布者发送的数据。 Assign 和 Sink 都遵循 Cancellable 协议.
最后的订阅者请参考官方文档:https://developer.apple.com/documentation/combine/subscriber
订阅者详细介绍与说明:http://www.55mx.com/ios/186.html
4、Subjects (对象)
Subjects 是一种遵循 Subject 协议的特殊的发布者。 这个协议要求 subjects 有一个 .send(_:) 方法,来允许开发者发送特定的值给订阅者或管道。
Subjects 可以通过调用 .send(_:) 方法来将值“注入”到流中, 这对于将现有的命令式的代码与 Combine 集成非常有用。
一个 subject 还可以向多个订阅者广播消息。 如果多个订阅者连接到一个 subject,它将在调用 send(_:) 时向多个订阅者发送值。 一个 subject 还经常用于连接或串联多个管道,特别是同时给多个管道发送值时。
每年框架都会更新,本文也没有办法及时的列出来所有的发布者相关的函数,下图是WWDC列出的最常用的热门函数单词。
上面图片中列出了Combine框架里使用频率比较高的相关功能,如果都掌握了,基本上可以随心所欲的使用Combine。
除非注明,网络人的文章均为原创,转载请以链接形式标明本文地址:https://www.55mx.com/post/183
《【Combine入门】初识Combine框架》的网友评论(1)