Я пытаюсь провести рефакторинг некоторого кода, в котором в настоящее время я смешиваю издателя Combine с закрытием обратного вызова.
Код выполняет запрос на вход в конечную точку REST. В случае сбоя я хочу, чтобы вызывающая функция получала полезную нагрузку (вызов), в случае успеха вызывающей стороне ничего делать не нужно. Так что это немного странная ситуация, когда вызывающая сторона заинтересована ТОЛЬКО в ошибке.
В настоящее время я делаю это так (упрощенный код):
import Foundation
import Combine
struct Challenge {
let id: Int
}
struct MyError: Error {
let challenge: Challenge?
}
class UserManager {
@Published var user: String?
private func doRequest(username: String, password: String) -> AnyPublisher<String, MyError> {
// In the real world this does a request to a REST API, returning either a User object or an error containing a Challenge.
return Fail(outputType: String.self, failure: MyError(challenge: Challenge(id: 1)))
.eraseToAnyPublisher()
}
func login(username: String, password: String, next: @escaping (Challenge) -> ()) -> AnyCancellable {
doRequest(username: username, password: password)
.catch { error -> AnyPublisher<String, Never> in
if let challenge = error.challenge {
next(challenge)
}
return Empty().eraseToAnyPublisher()
}
.sink(
receiveValue: { [weak self] user in
self?.user = user
}
)
}
}
let userManager = UserManager()
var anyCancellables = Set<AnyCancellable>()
userManager.login(username: "hello", password: "world") { challenge in
print("received challenge!")
}
.store(in: &anyCancellables)
Как видите, функция login
принимает параметр с именем next
, а издатель сам обрабатывает значение внутри оператора sink
. Не очень приятно объединять издателя с таким замыканием, в идеале я бы хотел, чтобы функция login
имела такую подпись:
func login(username: String, password: String) -> AnyPublisher<Challenge, Never>
Но когда я хочу написать такую функцию, я Я столкнулся с проблемой, когда я не могу использовать sink
внутри самой функции login
, так как это преобразует возвращаемый тип в AnyCancellable
. По сути, я хочу превратить ошибку в вывод издателя, а также обработать фактическое значение внутри функции login
. Как это возможно?