Combine 框架是苹果自己的响应式编程的框架, 至于什么是响应式编程,我这里就不再赘述,可以参考一下 ReactiveSwift RxSwift等知名的框架
言归正传: 直接上Demo: SimpleCombine 别忘了直接给个Star
开源框架 Open
SimpleCombine 是我根据自己的理解拆分的Combine框架, 组织架构不一定完全和Combine一致, 不过Combine的基本流程和思路已经梳理清楚, 阅读起来应该会比较清晰
先上使用案例:
func normalDemo() {
// 创建发布者
let up = SimplePassthroughSubject<String, Never>()
// 自动回收内存的容器, 需要让持有
var cancellable: [SimpleAnyCancellable] = []
// 订阅1 二者选其一
up.sink { str in
print("------\(str)-----")
}.store(in: &cancellable)
// 订阅2
let sub = SimpleSubscribers.Sink<String, Never> { _ in
} receiveValue: { input in
print(input)
}
sub.store(in: &cancellable)
up.receive(subscriber: sub)
// 符号操作
up.filter { output in
return output == "王金山"
}.sink { output in
print("输出的数据是: " + output)
}.store(in: &cancellable)
// 发送数据
up.send("王金山")
}
下面先看下 架构图
消息流程图
一: 架构图类讲解
首先需要记住几个概念: 发布者 订阅者 契约 管道 符号, 后面的所有内容都是按照这几个概念展开的
- 发布者: 整个消息的起始发布者
- 订阅者: 接收消息的使用者
- 契约: 管道能够处理的协议能力,是发布者和订阅者之间进行数据交互需要的一些行为,最终交给管道,在管道内完成
- 管道: 消息传递,沟通发布者和订阅者的桥梁,消息都是通过管道发送出去的
- 符号: 其实是特殊的发布者,他负责对发布者的进行一系列的限制措施,比如过滤,跳过发送等等
下面根据我自己实现的简单骨架类来带你起飞 SimpleCombine
1.1 发布者
发布者协议:
// 发布者协议
public protocol SimplePublisher {
associatedtype Output
associatedtype Failure: Error
// 接收订阅者
func receive<Subscriber: SimpleSubscriber>(subscriber: Subscriber)
where Failure == Subscriber.Failure, Output == Subscriber.Input
}
// 发布者需要的订阅能力
extension SimplePublisher {
public func subscribe<Subscriber: SimpleSubscriber>(_ subscriber: Subscriber)
where Failure == Subscriber.Failure, Output == Subscriber.Input {
receive(subscriber: subscriber)
}
}
扩充发布者默认的基枚举
// 所有的发布者
public enum SimplePublishers {}
内置的默认发布者 SimplePassthroughSubject遵守 SimpleSubject 他有几个能力: 1, 接收订阅者:
public func receive<Downstream: SimpleSubscriber>(subscriber: Downstream)
where Output == Downstream.Input, Failure == Downstream.Failure {}
2, 主送发送消息
public func send(_ input: Output) {}
3, 关联管道,让管道处理数据然后和订阅者数据交互
// 在接收订阅者方法中实现
let conduit = Conduit(parent: self, downstream: subscriber)
subscriber.receive(subscription: conduit)
1.2 订阅者
订阅者都遵守订阅协议:
public protocol SimpleSubscriber: SimpleCombineIdentifierConvertible {
func receive(subscription: SimpleSubscription)
func receive(_ input: Input) -> SimpleSubscribers.Demand
func receive(completion: SimpleSubscribers.Completion<Failure>)
}
扩展订阅的基枚举
public enum SimpleSubscribers {}
内置的两个订阅者 Sink 和 Assign 比如 Skin 具备以下能力 1, 接收发布者发送过来的锲约管道信息
public func receive(subscription: SimpleSubscription) {}
2, 接收管道具体处理完成的数据
public func receive(_ value: Input) -> SimpleSubscribers.Demand {}
1.3 契约
契约其实是一个协议,他主要是让管道去遵守协议,然后发布者把遵守协议的管道发送给订阅者,然后订阅者在收到契约后决定管道处理数据的方案
public protocol SimpleSubscription: SimpleCancellable, SimpleCombineIdentifierConvertible {
func request(_ demand: SimpleSubscribers.Demand)
}
1.4 管道
管道是处理数据,传递数据的地方,遵守契约协议 他有以下能力 1, 请求数据,然后发送数据给订阅者
override func request(_ demand: SimpleSubscribers.Demand) {
请求发布者原始数据,回传给订阅者
}
2, 处理发布者主动发送的数据,回传给订阅者
override func offer(_ output: Output) {
请求发布者的主动数据发送给订阅者
}
1.5 符号
符号主要是处理发布者消息,比如过滤发布者消息等等 遵守发布者协议,实现接收订阅者协议,然后创建自己的管道,让管道去处理数据
public struct Filter<Upstream: SimplePublisher>: SimplePublisher {
public func receive<Subscriber>(subscriber: Subscriber) where Subscriber : SimpleSubscriber, Upstream.Failure == Subscriber.Failure, Upstream.Output == Subscriber.Input {
upstream.subscribe(Inner(downstream: subscriber, filter: isIncluded)) // Inner 就是自己的管道
}
}
管道处理符号应该过滤的情况
func receive(_ input: Upstream.Output) -> SimpleSubscribers.Demand {
if filter(input) { // 判断满足的条件
return downstream.receive(input)
}
return .max(1)
}
概念和概念中主流代码已经简单介绍完毕,下面给出按照Demo梳理的数据流向伪代码 代码流
------- 发布者侧--------
//1 发布者方法订阅
public func sink(receiveValue: @escaping (Output) -> Void) -> SimpleAnyCancellable {
//2触发发布者的订阅代理
func receive<Subscriber: SimpleSubscriber>(subscriber: Subscriber)
}
//3 发布者接收订阅者
public func receive<Downstream: SimpleSubscriber>(subscriber: Downstream) {
// 创建管道
// 4,把管道发给订阅者
subscriber.receive(subscription: conduit)
}
------- 订阅者侧--------
// 5, 订阅者收到管道和契约
public func receive(subscription: SimpleSubscription) {
// 6, 根据契约内容,让契约发起请求数据需要,然后管道处理数据
subscription.request(.unlimited)
}
------- 管道侧--------
override func request(_ demand: SimpleSubscribers.Demand) {
// 7, 处理数据,发送数据
}
------- 订阅者侧--------
public func receive(_ value: Input) -> SimpleSubscribers.Demand {
// 8,接收到管道发来的数据,执行方法回调
}
// 特殊的发布者
发布者执行
up.send("王金山")
// 发布者侧
public func send(_ input: Output) {
let downstreams = self.downstreams
downstreams.forEach { conduit in
conduit.offer(input)
}
}
// 管道侧
override func offer(_ output: Output) {
// 回到 7, 处理数据,发送数据
}
到此Combine的整个框架的基本思路梳理完成, 剩下的就是缝缝补补
二: 扩展 UIKit
思路就是扩展 SimplePublishers 遵守SimplePublisher 创建自定义的管道,在管道中处理数据 比如给 UIButton 加publisher 举例子
extension UIButton {
var tapPublisher: SimpleAnyPublisher<UIControl, Never> {
return controlEventPublisher(for: .touchUpInside)
}
}
public extension UIControl {
func controlEventPublisher(for events: UIControl.Event) -> SimpleAnyPublisher<UIControl, Never> {
return SimplePublishers.ControlEvent(control: self, events: events).eraseToAnyPublisher()
}
}
struct ControlEvent<Control: UIControl>: SimplePublisher {
public func receive<Subscriber: SimpleSubscriber>(subscriber: Subscriber) where Subscriber.Failure == Failure, Subscriber.Input == Output {
let subscription = CocoSubscription(subscriber: subscriber,control: control,event: controlEvents)
subscriber.receive(subscription: subscription)
}
}
private final class CocoSubscription: SimpleSubscription {
init(subscriber: Subscriber, control: Control, event: Control.Event) {
control.addTarget(self, action: #selector(handleEvent(control:)), for: event)
}
实战部分在下一篇的内容中!!! Combine API实战