在 Swift 的 Combine 发布者中包装异步代码

Wrapping asynchronous code in Swift's Combine publisher

我有一个名为 QueryObserver 的 class,它可以随时间产生 多个结果 ,作为回调(闭包)返回。你这样使用它:

let observer = QueryObserver<ModelType>(query: query) { result in
  switch result {
  case .success(let value):
    print("result: \(value)")
  case .failure(let error):
    print("error: \(error)")
  }
}

QueryObserver 实际上是 Firebase Firestore 笨拙的 query.addSnapshotListener 功能的包装器,如果您想知道的话。使用现代 Result 类型而不是具有多个可选参数的回调。)

在一个较旧的项目中,我正在使用 ReactiveKit 并且有一个扩展将所有这些变成 Signal,就像这样:

extension QueryObserver {
  public static func asSignal(query: Query) -> Signal<[T], Error> {
    return Signal { observer in
      let queryObserver = QueryObserver<T>(query: query) { result in
        switch result {
        case .success(let value):
          observer.receive(value)
        case .failure(let error):
          if let firestoreError = error as? FirestoreError, case .noSnapshot = firestoreError {
            observer.receive([])
          } else {
            observer.receive(completion: .failure(error))
          }
        }
      }

      return BlockDisposable {
        queryObserver.stopListening()
      }
    }
  }
}

虽然在一个全新的项目中,我正在使用 Combine 并试图重写它。到目前为止,我已经设法写了这个,但它不起作用。这是有道理的:observer 没有被任何东西保留,所以它立即被释放,没有任何反应。

extension QueryObserver {
  public static func asSignal(query: Query) -> AnyPublisher<[T], Error> {
    let signal = PassthroughSubject<[T], Error>()

    let observer = QueryObserver<T>(query: query) { result in
      switch result {
      case .success(let value):
        print("SUCCESS!")
        signal.send(value)
      case .failure(let error):
        if let firestoreError = error as? FirestoreError, case .noSnapshot = firestoreError {
          signal.send([])
        } else {
          signal.send(completion: .failure(error))
        }
      }
    }

    return signal.eraseToAnyPublisher()
  }
}

如何使用 Combine 版本?如何包装现有的异步代码?我发现的唯一示例使用 Future 进行一次性回调,但随着时间的推移我正在处理多个值。

基本上我正在寻找 this 的 ReactiveKit-to-Combine 版本。

查看 https://github.com/DeclarativeHub/ReactiveKit/issues/251#issuecomment-575907641 一个方便的组合版本的信号,像这样使用:

let signal = Signal<Int, TestError> { subscriber in
    subscriber.receive(1)
    subscriber.receive(2)
    subscriber.receive(completion: .finished)
    return Combine.AnyCancellable {
        print("Cancelled")
    }
}