问题
I am trying to use 2 publishers and have them stream to 1 publisher that is mapped from both values.
My code is:
class ViewModel {
let email = CurrentValueSubject<String, Never>("")
lazy var isEmailValid = email.map { self.validateEmail(email: $0) }
let password = CurrentValueSubject<String, Never>("")
lazy var isPasswordCorrect = password.map {
self.validatePassword(password: $0)
}
let canLogin: CurrentValueSubject<Bool, Never>
private func validateEmail(email: String) -> Bool {
return email == "1234@gmail.com"
}
private func validatePassword(password: String) -> Bool {
return password == "1234"
}
init() {
canLogin = Publishers
.CombineLatest(isEmailValid, isPasswordCorrect)
.map { $0 && $1 }
}
}
Then in the init I get this error:
//error: Cannot assign value of type
'Publishers.Map<Publishers.CombineLatest<Publishers.Map<CurrentValueSubject<String, Never>,
Bool>, Publishers.Map<CurrentValueSubject<String, Never>, Bool>>, Bool>' to type 'CurrentValueSubject<Bool, Never>'
I am new to combine so I find it a little confusing. How should I achieve, from the code above, the combination of 2 publishers isEmailValid and isPasswordCorrect, into 1 publisher that is a CurrentValueSubject<Bool, Never>?
回答1:
A CurrentValueSubject is:
A subject that wraps a single value and publishes a new element whenever the value changes.
Your canLogin
is certainly not a CurrentValueSubject
. It is the result of combining two other publishers with the CombineLatest
operator, and then mapping the combined publisher to yet another publisher.
In the language of the Swift type system, this kind of publisher is called:
Publishers.Map<Publishers.CombineLatest<Publishers.Map<CurrentValueSubject<String, Never>, Bool>, Publishers.Map<CurrentValueSubject<String, Never>, Bool>>, Bool>
Obviously, no one would declare a property with a type like that, so we use eraseToAnyPublisher
to get ourselves an AnyPublisher
, to say that we don't actually care what type of publisher it is.
let canLogin: AnyPublisher<Bool, Never>
...
canLogin = Publishers
.CombineLatest(isEmailValid, isPasswordCorrect)
.map { $0 && $1 }
.eraseToAnyPublisher()
回答2:
You've declared the type of canLogin
completely incorrectly.
It needs to be an AnyPublisher
, which you can get by simply calling eraseToAnyPublisher
on the map
.
lazy var canLogin: AnyPublisher<Bool, Never> = isEmailValid.combineLatest(isPasswordCorrect).map { $0 && $1 }.eraseToAnyPublisher()
来源:https://stackoverflow.com/questions/65072163/how-to-use-publishers-combinelatest-to-get-1-publisher