gpt4 book ai didi

ios - 如何用一次性发布者包装委托(delegate)模式?

转载 作者:行者123 更新时间:2023-12-05 01:09:40 26 4
gpt4 key购买 nike

通常我们可以通过使用 Future 将我们的异步代码包装在单次发布者中来桥接我们的异步代码和组合:

func readEmail() -> AnyPublisher<[String], Error> {
Future { promise in
self.emailManager.readEmail() { result, error in
if let error = error {
promise(.failure(error))
} else {
promise(.success(result))
}
}
}.eraseToAnyPublisher()
}

另一方面,如果我们包装委托(delegate)模式(而不是异步回调),它是 recommended to use a PassthroughSubject ,因为这些方法可以被多次触发:

final class LocationHeadingProxy: NSObject, CLLocationManagerDelegate {

private let headingPublisher: PassthroughSubject<CLHeading, Error>

override init() {
headingPublisher = PassthroughSubject<CLHeading, Error>()
// ...
}

func locationManager(_ manager: CLLocationManager, didUpdateHeading newHeading: CLHeading) {
headingPublisher.send(newHeading)
}
}

但是,我正在尝试创建 one-shot publisher它包装了现有的委托(delegate)模式。原因是我正在启动像 connect() 这样的方法,并且我希望立即发生成功或失败。我不希望 future 的更新影响管道。

例如,假设我正在使用 WKExtendedRuntimeSession 并将 .start() 方法包装在下面的 startSession() 中。如果我成功包装了它,我应该可以像这样使用它:

manager.startSession()
.sink(
receiveCompletion: { result in
if result.isError {
showFailureToStartScreen()
}
},
receiveValue: { value in
showStartedSessionScreen()
})
.store(in: &cancellables)

一次性发布者之所以有用,是因为我们希望在调用该方法后立即调用以下两种方法之一:

  • 成功:extendedRuntimeSessionDidStart(_:)
  • 失败:extendedRuntimeSession(_:didInvalidateWith:error:)

此外,当 session 暂停(或我们自己终止)时,我们不希望像 showFailureToStartScreen() 这样的副作用随机发生。我们希望在代码的其他地方明确处理它们。因此,在这里使用一次性管道是有益的,因此我们可以保证 sink 只被调用一次。


我意识到做到这一点的一种方法是使用 Future,存储对 Promise 的引用,然后在稍后调用 promise,但这似乎充其量是hacky:

class Manager: NSObject, WKExtendedRuntimeSessionDelegate {
var session: WKExtendedRuntimeSession?
var tempPromise: Future<Void, Error>.Promise?

func startSession() -> AnyPublisher<Void, Error> {
session = WKExtendedRuntimeSession()
session?.delegate = self
return Future { promise in
tempPromise = promise
session?.start()
}.eraseToAnyPublisher()
}

func extendedRuntimeSessionDidStart(_ extendedRuntimeSession: WKExtendedRuntimeSession) {
tempPromise?(.success(()))
tempPromise = nil
}

func extendedRuntimeSession(_ extendedRuntimeSession: WKExtendedRuntimeSession, didInvalidateWith reason: WKExtendedRuntimeSessionInvalidationReason, error: Error?) {
if let error = error {
tempPromise?(.failure(error))
}
tempPromise = nil
}
}

这真的是与代表 + 一次性发布者合作的最优雅的方式,还是在 Combine 中有更优雅的方式来做到这一点?


供引用,PromiseKit也有一个类似于 Future.init 的 API。即,Promise.init(resolver:) .然而,PromiseKit 似乎也原生支持我上面描述的功能,他们的 pending()函数(example):

  func startSession() -> Promise {
let (promise, resolver) = Promise.pending()
tempPromiseResolver = resolver

session = WKExtendedRuntimeSession()
session?.delegate = self
session?.start()

return promise
}

最佳答案

您可以使用 .first() 运算符确保一次性发布者:

let subject = PassthroughSubject<Int, Never>()

let publisher = subject.first()

let c = publisher.sink(receiveCompletion: {
print($0)
}, receiveValue: {
print($0)
})

subject.send(1)
subject.send(2)

输出将是:

1
finished

关于ios - 如何用一次性发布者包装委托(delegate)模式?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/64921656/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com