阅读 278

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 decodereplaceError是操作(Operator)

    • assign是订阅者(Subscriber)

    • cancellable是一个Cancellable可以用其cancel()方法来取消相关操作的。

image-20211022152214994.png

  • 首先订阅者请求订阅(Subscribe)

  • 然后发布者返回订阅内容(Subscription)

  • 再次订阅者请求数据

  • 发布者不断发布数据给订阅者

  • 发布者发送完成事件,结束整个流程

发布者(Publisher)

PublisherCombine框架的一个很重要的的协议。Publisher是发布事件给一个或者多个订阅者。好比使用NotificationCenter编程时调用NotificationCenter.default.post来发布事件,这个发布事件的对象就相当于一个Publisher, 只不过不是使用Combine框架而已。

一个Publisher可以传递事件给一个或者多个订阅者(Subscriber)实例。 Publisher包含了Output类型和Failure错误类型。可以通过操作(Operator)来转换这个Output类型和Failure类型,订阅者(Subscriber)的IntputFailure类型要符合PublisherOutput类型和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类型, NeverFailure类型,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


文章分类
后端
版权声明:本站是系统测试站点,无实际运营。本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 XXXXXXo@163.com 举报,一经查实,本站将立刻删除。
相关推荐