Publisher协议

在 Swift Combine 框架中,Publisher 协议定义了一个可以发布数据流的类型。符合 Publisher 协议的类型会对外发布数据事件,订阅者(Subscriber)可以通过订阅这些发布者来接收事件。 Publisher 协议的核心是允许处理数据流和异步事件,并提供了丰富的操作符来对数据流进行组合和变换。

Publisher 协议的主要组成

Publisher 协议定义如下:

protocol Publisher {
    associatedtype Output  // 发布的数据类型
    associatedtype Failure: Error  // 可能发生的错误类型

    // 发布数据事件的核心方法
    func receive<S>(subscriber: S) where S: Subscriber, S.Input == Output, S.Failure == Failure
}
  • Output: 定义发布者发布的数据类型。

  • Failure: 定义发布者可能遇到的错误类型。Failure 必须遵循 Error 协议,若发布者不产生错误,可以将 Failure 定义为 Never

Publisher 协议中的事件类型

Publisher 会对外发布三种类型的事件:

  1. 值事件(Value Event): 包含实际的数据值,表示流中的一个数据。

  2. 完成事件(Completion Event): 表示发布者的数据流已经完成。

  3. 失败事件(Failure Event): 表示发布过程中出现错误,流终止。

创建自定义 Publisher

可以实现一个自定义的 Publisher 来发布数据流。例如,这里定义了一个简单的倒计时发布者:

struct CountdownPublisher: Publisher {
    typealias Output = Int
    typealias Failure = Never

    let count: Int

    func receive<S>(subscriber: S) where S : Subscriber, CountdownPublisher.Failure == S.Failure, CountdownPublisher.Output == S.Input {
        var currentCount = count
        let subscription = TimerSubscription(subscriber: subscriber, currentCount: currentCount)
        subscriber.receive(subscription: subscription)
    }
}

// 实现自定义的 Subscription 来支持倒计时发布
class TimerSubscription<S: Subscriber>: Subscription where S.Input == Int {
    var subscriber: S?
    var currentCount: Int

    init(subscriber: S, currentCount: Int) {
        self.subscriber = subscriber
        self.currentCount = currentCount
    }

    func request(_ demand: Subscribers.Demand) {
        // 模拟数据流
        while currentCount > 0, demand > 0 {
            _ = subscriber?.receive(currentCount)
            currentCount -= 1
        }
        subscriber?.receive(completion: .finished)
    }

    func cancel() {
        subscriber = nil
    }
}

Publisher 的操作符

Combine 框架为 Publisher 提供了丰富的操作符,可以方便地对数据流进行处理和组合:

  • 转换操作符: map, flatMap, filter

  • 组合操作符: merge, zip, combineLatest

  • 错误处理: catch, retry

  • 连接操作符: share, multicast, connect

例子

下面是使用 Publisher 操作符的一个示例,模拟从一个数组中发布数据:

let numbers = [1, 2, 3, 4, 5]
let publisher = numbers.publisher

publisher
    .map { $0 * 2 }
    .sink(receiveCompletion: { print("Completed: \($0)") },
          receiveValue: { print("Received value: \($0)") })

总结

Publisher 协议在 Combine 中是核心组件,它允许定义并发布数据流,并通过操作符对数据流进行变换和组合。通过 Publisher 的事件机制,可以实现响应式编程。

Last updated