Когда вы создаете фактического издателя с помощью let future
, добавьте оператор .share
, чтобы два ваших подписчика подписались на один разделенный конвейер.
EDIT: Как я уже сказал в своих комментариях, я бы внес некоторые другие изменения в ваш конвейер. Вот предлагаемая переписать. Некоторые из этих изменений являются стилистическими c / cosmeti c, как иллюстрация того, как я пишу код Combine; Вы можете принять это или оставить. Но другие вещи в значительной степени de rigueur . Вам нужны отложенные оболочки вокруг ваших Futures, чтобы предотвратить преждевременное сетевое взаимодействие (то есть до того, как произойдет подписка). Вам нужно store
конвейер, иначе он go перестанет существовать, прежде чем сеть сможет начать работу. Я также заменил .handleEvents
вашего второго подписчика, хотя, если вы используете вышеуказанное решение с .share
, вы все равно можете использовать второго подписчика, если действительно хотите. Это полный пример; вы можете просто скопировать и вставить его прямо в проект.
class ViewController: UIViewController {
enum ServerError: Error {
case authenticationFailed
case noConnection
case timeout
}
var storage = Set<AnyCancellable>()
func authenticate(username: String, password: String) -> AnyPublisher<Bool, ServerError> {
Deferred {
Future { promise in
print("Calling server to authenticate")
DispatchQueue.main.async {
promise(.success(true))
}
}
}.eraseToAnyPublisher()
}
func downloadUserInfo(username: String) -> AnyPublisher<String, ServerError> {
Deferred {
Future { promise in
print("Downloading user info")
DispatchQueue.main.async {
promise(.success("decoded user data"))
}
}
}.eraseToAnyPublisher()
}
func authenticateAndDownloadUserInfo(username: String, password: String) -> AnyPublisher<String, ServerError> {
let authenticate = self.authenticate(username: username, password: password)
let pipeline = authenticate.flatMap { isAuthenticated -> AnyPublisher<String, ServerError> in
if isAuthenticated {
return self.downloadUserInfo(username: username)
} else {
return Fail<String, ServerError>(error: .authenticationFailed).eraseToAnyPublisher()
}
}
return pipeline.eraseToAnyPublisher()
}
override func viewDidLoad() {
super.viewDidLoad()
authenticateAndDownloadUserInfo(username: "stack", password: "overflow")
.handleEvents(
receiveSubscription: { _ in print("start the spinner!") },
receiveCompletion: { _ in print("stop the spinner!") }
).sink(receiveCompletion: {
switch $0 {
case .finished:
print("Completed without errors.")
case .failure(let error):
print("received error: '\(error)'")
}
}) {
print("received userInfo: '\($0)'")
}.store(in: &self.storage)
}
}
Вывод:
start the spinner!
Calling server to authenticate
Downloading user info
received userInfo: 'decoded user data'
stop the spinner!
Completed without errors.