SwiftUI学习(7)-Combine入门Part I-Publisher
简介
用苹果自己话说:Combine
提供了APP处理事件的响应式声明框架。相较于之前的继承Delegate
回调或者使用完成闭包
参数,你可以创建一个从事件源头开的是处理链。该处理链每一个环节都是一个Combine运算
,每个运算都会对上一个运算传递出来的内容进行特定处理。
听起来很抽象, 举个不严谨的例子, 使用URLSession请求网络解析JSON数据
之前的做法:
let task = URLSession.shared.dataTask(with: URL(string:"https://www.url.com")!) { data, resp, error in guard error == nil else{ // 处理错误 return } guard data != nil else{ // 处理错误 return } let decoder = JSONDecoder() if let json = try? decoder.decode(JsonTarget.self, from: data!){ self.response = json }else{ self.response = Json(code: -1) } } task.resume() 复制代码
Combine
的写法:let publisher = URLSession.shared.dataTaskPublisher(for:URL(string:"https://www.url.com")!) let cancellable = .map({return $0.data}) .decode(type: JsonTarget.self, decoder: JSONDecoder()) .replaceError(with: JsonTarget(code: -1)) .assign(to: \.resposne, on: self) 复制代码
Combine
的流程如下:URLSession.shared.dataTaskPublisher
返回的是发布者(Publisher)map
decode
和replaceError
是操作(Operator)assign
是订阅者(Subscriber)cancellable
是一个Cancellable
可以用其cancel()
方法来取消相关操作的。
首先订阅者请求订阅(Subscribe)
然后发布者返回订阅内容(Subscription)
再次订阅者请求数据
发布者不断发布数据给订阅者
发布者发送完成事件,结束整个流程
发布者(Publisher)
Publisher
是Combine
框架的一个很重要的的协议。Publisher
是发布事件给一个或者多个订阅者。好比使用NotificationCenter
编程时调用NotificationCenter.default.post
来发布事件,这个发布事件的对象就相当于一个Publisher
, 只不过不是使用Combine
框架而已。
一个Publisher
可以传递事件给一个或者多个订阅者(Subscriber)实例。 Publisher
包含了Output
类型和Failure
错误类型。可以通过操作(Operator)来转换这个Output
类型和Failure
类型,订阅者(Subscriber)的Intput
和Failure
类型要符合Publisher
的Output
类型和Failure
错误类型或者经过操作(Operator)转换后的Output
类型和Failure
错误类型才可以订阅。
Publisher协议
先看看Publisher
协议:
public protocol Publisher { associatedtype Output associatedtype Failure : Error func receive<S>(subscriber: S) where S : Subscriber, Self.Failure == S.Failure, Self.Output == S.Input } 复制代码
协议包含Output
类型和错误Failure
类型,以及一个接受订阅者的方法。简单实现一个自定义的Publisher
,如下:
class TestPublisher: Publisher{ func receive<S>(subscriber: S) where S : Subscriber, Never == S.Failure, Int == S.Input { subscriber.receive(1) DispatchQueue.global().asyncAfter(deadline: DispatchTime.now() + 2){ subscriber.receive(2) subscriber.receive(completion: .finished) } } typealias Output = Int typealias Failure = Never } let testPublisher = TestPublisher() testPublisher.sink(receiveCompletion: { print("completed", $0)}, receiveValue: {print("received value", $0)}) 复制代码
输出如下:
received value 1 received value 2 completed finished 复制代码
在subscriber.receive(completion: .finished)
之后加一行subscriber.receive(3)
,修改后:
... DispatchQueue.global().asyncAfter(deadline: DispatchTime.now() + 2){ subscriber.receive(2) subscriber.receive(completion: .finished) subscriber.receive(3) } ... 复制代码
运行下:
received value 1 received value 2 completed finished 复制代码
可以看到当subscriber.receive(completion:)
调用完了后,流程已经结束了,之后在发送事件,订阅者无法收到的事件的。
便捷发布者(Convenience Publishers)
Swift内置了一些便捷的Publisher, 方便快速使用。
Future
从名字看就是未来某个时刻会发出一个事件然后就结束。
举个栗子,在10秒后发出一个1:
let future = Future<Int, Never>{ promise in DispatchQueue.global().asyncAfter(deadline: DispatchTime.now() + 10) { promise(.success(1)) } } future.sink(receiveCompletion: { print("Received value", $0)}, receiveValue: { print("Receive value:", $0)}).store(in: &store) 复制代码
运行10s后看到输出:
Receive value: 1 Received value finished 复制代码
Future<Int, Never>
: Int表示Publisher
的Output类型, Never
是Failure
类型,Never
表示不会发生错误。构造函数里的promise参数用来成功或者失败发送事件。
Just
这是一个马上发出事件的Publisher
。
var store = Set<AnyCancellable>() Just(1).sink(receiveCompletion: { print("Received value", $0)}, receiveValue: { print("Receive value:", $0)}).store(in: &store) 复制代码
运行后马上看到输出:
Receive value: 1 Received value finished 复制代码
Deferred
这个是延时创建Publisher
,当被订阅的时候才会创建Publisher
。
举个例子, 5s后发布一个:
let date = Date() let publisher = Deferred { () -> Just<Int> in print("Create Pulisher", 0 - date.timeIntervalSinceNow) return Just(1) } DispatchQueue.global().asyncAfter(deadline: DispatchTime.now() + 5){ print("Subscribe", 0 - date.timeIntervalSinceNow) publisher.sink(receiveCompletion: { print("completed", $0, 0 - date.timeIntervalSinceNow)}, receiveValue: {print("received value", $0, 0 - date.timeIntervalSinceNow)}) } print("Run", 0 - date.timeIntervalSinceNow) 复制代码
输出如下:
Run 0.0022840499877929688 Subscribe 5.497779011726379 Create Pulisher 5.497919082641602 received value 1 5.4987300634384155 completed finished 5.49881899356842 复制代码
可以看到当调用sink
后才创建Just Publisher
。
Empty
Empty
不会发送任何内容,当订阅后立刻发送complete事件。
Fail
Fail
不会发送任何内容,当订阅后立刻发送指定Error。
Record
Record
可以先用Record.Recording
来记录一系列事件,稍后在通过Record
发送给订阅者。
举个例子
var recording = Record<Int, Never>.Recording() DispatchQueue.global().asyncAfter(deadline: DispatchTime.now() + 1) { let publisher = Record<Int, Never>(recording: recording) publisher.sink(receiveCompletion: { print("Received completed", $0)}, receiveValue: { print("Received Value", $0)}) } DispatchQueue.global().asyncAfter(deadline: DispatchTime.now() + 2) { let publisher2 = Record<Int, Never>(recording: recording) publisher2.sink(receiveCompletion: { print("Received 2 completed", $0)}, receiveValue: { print("Received 2 Value", $0)}) } DispatchQueue.global().asyncAfter(deadline: DispatchTime.now() + 3) { let publisher3 = Record<Int, Never>(recording: recording) publisher3.sink(receiveCompletion: { print("Received 3 completed", $0)}, receiveValue: { print("Received 3 Value", $0)}) } recording.receive(1) recording.receive(2) recording.receive(completion: .finished) 复制代码
共用一个Recording记录了发送1,2和finish事件。创建3个Record来发布Recording的内容。
作者:小小肉丸
链接:https://juejin.cn/post/7022535696917217316