Reckoner
Reckoner

Reputation: 1041

send() gets called only for the first time in combine framework iOS

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

Answers (1)

matt
matt

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

Related Questions