paint-brush
Swift 中有用的发布者:基本指南经过@vadimchistiakov
1,389 讀數
1,389 讀數

Swift 中有用的发布者:基本指南

经过 Vadim Chistiakov11m2023/01/18
Read on Terminal Reader

太長; 讀書

发布者是一种符合 `Publisher` 协议的类型。它负责为订阅者提供价值流。发布者可以随时间发出一个或多个值,也可以完成或失败。 Combine 框架提供了许多内置的发布者。
featured image - Swift 中有用的发布者:基本指南
Vadim Chistiakov HackerNoon profile picture
0-item

概述

在 Combine 框架中,发布者是一种符合Publisher协议的类型。它负责为订阅者提供价值流。 Publisher协议定义了两个关联类型: OutputFailure ,分别表示发布者可以发出的值的类型和可以抛出的错误的类型。


发布者可以随时间发出一个或多个值,也可以完成或失败。当订阅者订阅发布者时,发布者调用订阅者的receive(subscription:)方法并向其传递一个Subscription对象,订阅者可以使用该对象来控制值的流动。订阅者也可以调用发布者的receive(_:)方法来接收新值。


Combine 框架提供了一些内置的发布器,例如JustFailEmptyDeferredSequence ,可以用来创建各种类型的发布器。此外,您可以通过遵守Publisher协议并实现所需的方法来创建自己的自定义发布者。


发布者也可以组合在一起以创建更复杂的管道。 Combine 框架提供了许多可用于修改和合并发布者的内置运算符,例如mapfilterreduceflatMapzipmerge 。这些运算符由Publisher者协议扩展提供,可以在任何发布者上调用。


现在我想为您提供一些我在我的项目中使用的有用的发布者。

重复计时器发布者

要在 Swift 中实现使用具有自定义间隔的重复计时器的发布者,您可以使用 Foundation 框架中的Timer类。以下是如何执行此操作的示例:


RepeatingTimeSubscription符合Subscription协议:


 private class RepeatingTimerSubscription<S: Subscriber>: Subscription where S.Input == Void { private let interval: TimeInterval private let queue: DispatchQueue private var subscriber: S? private var timer: Timer? init(interval: TimeInterval, queue: DispatchQueue, subscriber: S) { self.interval = interval self.queue = queue self.subscriber = subscriber } func request(_ demand: Subscribers.Demand) { timer?.invalidate() timer = Timer.scheduledTimer( withTimeInterval: interval, repeats: true ) { [weak self] _ in self?.queue.async { _ = self?.subscriber?.receive() } } } func cancel() { timer?.invalidate() timer = nil subscriber = nil } }


RepeatingTimePublisher符合Publisher协议:


 import Foundation import Combine final class RepeatingTimerPublisher: Publisher { typealias Output = Void typealias Failure = Never private let interval: TimeInterval private let queue: DispatchQueue init(interval: TimeInterval, queue: DispatchQueue = .main) { self.interval = interval self.queue = queue } func receive<S>(subscriber: S) where S: Subscriber, Failure == S.Failure, Output == S.Input { let subscription = RepeatingTimerSubscription( interval: interval, queue: queue, subscriber: subscriber ) subscriber.receive(subscription: subscription) } }



要使用此发布者,您可以创建它的实例并使用Publisher协议的sink方法订阅它。


例如:

 private var cancellable: AnyCancellable? func subscribeOnTimer(interval: TimeInterval) { let publisher = RepeatingTimerPublisher(interval: interval) cancellable = publisher.sink { print("Timer fired!") } } //TEST THE METHOD subscribeOnTimer(interval: 5.0)


这将打印“定时器已触发!”每 5 秒。您可以通过对sink方法返回的AnyCancellable对象调用cancel方法来取消订阅。


例如:


 deinit { cancellable?.cancel() }


长轮询发布者

要在 Swift 中使用 Combine 框架实现长轮询,您可以创建一个发布者,它以指定的时间间隔发出网络请求,并将响应作为输出返回。以下是如何执行此操作的示例:


失败案例的自定义错误枚举。

 enum CustomError: Error { case invalidResponse case invalidDecoding case error }


LongPollingSubscription符合Subscription协议:


 private class LongPollingSubscription<S: Subscriber, Output: Decodable>: Subscription where S.Input == Output, S.Failure == CustomError { private let url: URL private let interval: TimeInterval private let decoder: JSONDecoder private var subscriber: S? private var timer: Timer? private var task: URLSessionDataTask? init( url: URL, interval: TimeInterval, subscriber: S, decoder: JSONDecoder = JSONDecoder() ) { self.url = url self.interval = interval self.subscriber = subscriber self.decoder = decoder } func request(_ demand: Subscribers.Demand) { timer?.invalidate() timer = Timer.scheduledTimer( withTimeInterval: interval, repeats: true ) { [weak self] _ in self?.makeRequest() } makeRequest() } func cancel() { timer?.invalidate() timer = nil task?.cancel() task = nil subscriber = nil } private func makeRequest() { task = URLSession.shared.dataTask(with: url) { [weak self] data, response, error in guard let self else { return } if let error = error as? S.Failure { self.subscriber?.receive( completion: .failure(error) ) return } guard let data else { self.subscriber?.receive( completion: .failure(.invalidResponse) ) return } do { let output = try self.decoder.decode( Output.self, from: data ) _ = self.subscriber?.receive(output) } catch { self.subscriber?.receive( completion: .failure(.invalidDecoding) ) } } task?.resume() } }


LongPollingPublisher符合Publisher协议:


 final class LongPollingPublisher<Output: Decodable>: Publisher { typealias Failure = CustomError private let url: URL private let interval: TimeInterval init(url: URL, interval: TimeInterval) { self.url = url self.interval = interval } func receive<S>(subscriber: S) where S: Subscriber, Failure == S.Failure, Output == S.Input { let subscription = LongPollingSubscription( url: url, interval: interval, subscriber: subscriber ) subscriber.receive(subscription: subscription) } }


<Output: Decodable>意味着您可以使用任何符合Decodable协议的通用类型的响应。


为了进行测试,您需要创建一个符合Decodable的模型。我使用https://pixabay.com/api的公共 API。


让它成为 PhotoResponse 结构:


 struct PhotoResponse: Decodable { struct Photo: Decodable { let user: String let id: Int let largeImageURL: String } let hits: [Photo] let total: Int }


要使用此发布者,您可以创建它的实例并使用Publisher协议的sink方法订阅它。例如:


 private var cancellable: AnyCancellable? private func pollingTest() { let url = URL(string: "https://pixabay.com/api/?key={your_key}")! let publisher = LongPollingPublisher<PhotoResponse>( url: url, interval: 5.0 ) cancellable = publisher.sink(receiveCompletion: { completion in switch completion { case .finished: print("Completed") case .failure(let error): print("Error: \(error)") } }, receiveValue: { response in print("Received response: \(response)") }) } //TEST THE METHOD pollingTest()


还有一件事

可以使用 Swift 中的 Combine 框架创建许多有用的自定义发布者。这里有一些例子:

  1. NotificationCenterPublisher :当指定通知发布到NotificationCenter时发出值的发布者。您可以使用此发布者对系统或特定于应用程序的事件做出反应,例如设备轮换或网络状态更改。
  2. KeyboardPublisher :在显示或隐藏键盘时发出值的发布者。当键盘显示或关闭时,您可以使用此发布器调整视图的布局。
  3. CoreLocationPublisher :当用户的位置发生变化时发出值的发布者。此发布者可用于跟踪用户的位置并根据他们的位置执行操作。
  4. UIControlEventPublisher :当UIControl (例如 UIButton 或 UITextField)上发生指定事件时发出值的发布者。这可用于以反应方式处理用户交互。

这些只是可以使用 Combine 框架创建的自定义发布者类型的几个例子。关键是了解您的应用程序的要求,并使用框架提供的可用构建块来创建满足这些要求的发布者。

综上所述

最后,需要注意的是,Combine 框架使用函数式反应式编程范式,这是一种随时间处理事件流的编程模型。发布者以及订阅者和运营商是此范例的核心构建块,它们使创建复杂且响应迅速的应用程序变得容易。