Reputation: 1041
Whenever I am typing something in TextField, I am getting the value through the 'sink' operator after making UITextField the publisher. However, now I want to validate that password and the below piece of code involves 'send()' that works only for the first time the validatePassword() method is called. After that 'send()' does not work. Any idea why is this happening? I have been exploring Combine for 4-5 days now and here is the gist:
1. Publishers, subscribers and operators.
2. publishers in ViewModel and subscribers in ViewController.
3. Send() is used for sending a value to subscriber.
Everything fits but it still doesn't get called after first time. Stuck on it for 3-4 hours now.
Here is the code for ViewController
viewModel.passwordValidationResult
.print("Debugging")
.sink { completion in
switch completion {
case .finished:
print("c")
return
case .failure(_):
print("f")
return
}
} receiveValue: { _ in
print("v")
}
.store(in: &bindings)
}
Here is the code for ViewModel
class ViewModel
{
private var bindings = Set<AnyCancellable>()
@Published var emailValueChanged: String = String()
@Published var passwordValueChanged: String = String()
let passwordValidationResult = PassthroughSubject<Void, Error>()
//input
func validatePassword(_ password: String) {
if password.count >= 6 {
passwordValidationResult.send()
}
passwordValidationResult.send(completion: .failure(PasswordError.shortPassword))
}
}
Upvotes: 0
Views: 696
Reputation: 535586
The problem is this line:
passwordValidationResult.send(completion: .failure(PasswordError.shortPassword))
A failure is, as the code says, a completion. It ends the pipeline, which can never be used for anything else. Your PassthroughSubject is now dead. That is why no more values can be sent.
The issue stems from this incorrect choice:
let passwordValidationResult = PassthroughSubject<Void, Error>()
You do not want your signals to be Void and Error, you want them to be something like true and false (for valid and invalid): in other words, you want a PassthroughSubject<Bool, Never>
. Or instead of a Bool you could use a dedicated enum with cases like .valid
and .invalid
. Or you could pass a Result object which itself has a .success
case and a .failure
case.
In any case do not send a completion! You want to be able to keep sending signals indefinitely, not cut yourself off after the first "failure".
Upvotes: 1