Reputation: 8388
I am working on a form build with Storyboard(UIKit). I have 4 input fields and when it is valid I will submit the data to a UI and show response on UI. I have created a ViewModel and added the validation code there. Following is my validation code:
@Published public var firstName = ""
@Published public var lastName = ""
@Published public var phoneNumber = ""
@Published public var emailAddress = ""
var isFirstNameValidPublisher: AnyPublisher<Bool, Never> {
$firstName
.map { name in
return name.count >= 3
}
.eraseToAnyPublisher()
}
var isLastNameValidPublisher: AnyPublisher<Bool, Never> {
$lastName
.map { name in
return name.count >= 3
}
.eraseToAnyPublisher()
}
var isUserEmailValidPublisher: AnyPublisher<Bool, Never> {
$emailAddress
.map { email in
let emailPredicate = NSPredicate(format:"SELF MATCHES %@", "[A-Z0-9a-z._%+-]+@[A-Za-z0-9.-]+\\.[A-Za-z]{2,64}")
return emailPredicate.evaluate(with: email)
}
.eraseToAnyPublisher()
}
var isPhoneNumberValidPublisher: AnyPublisher<Bool, Never> {
$phoneNumber
.map { phoneNumber in
return phoneNumber.count >= 8
}
.eraseToAnyPublisher()
}
public func isValidForm(firstName: String, lastName: String, phoneNum: String, emailAddress: String, isConsent: Bool) -> AnyPublisher<Bool, Never> {
self.firstName = firstName
self.lastName = lastName
self.phoneNumber = phoneNum
self.emailAddress = emailAddress
return Publishers.CombineLatest4(
isFirstNameValidPublisher,
isLastNameValidPublisher,
isUserEmailValidPublisher,
isPhoneNumberValidPublisher)
.map { isFirstNameValid, isLastNameValid, isPhoneNumberValid, isEmailValid in
return isFirstNameValid && isLastNameValid && isPhoneNumberValid && isEmailValid && isConsent
}
.eraseToAnyPublisher()
}
I Am trying to use this on submit button click in my ViewController as following:
cancellable = dependencies.leadConsumptionUseCase.isValidForm(firstName: firstNameTextView.text, lastName: lastNameTextView.text, phoneNum: phoneNumberTextView.text, emailAddress: emailTextView.text, isConsent: true).collect().sink(receiveCompletion: { completion in
print("completed")
print(completion)
}, receiveValue: { res in
print("result")
print(res)
})
I am able to validate the fields in ViewModel but somehow I am not getting any response in my ViewController. Also I am not sure how and where should I add the api call. I have already created another service class where I have an api call that will return the response as AnyCancellable. Most of the related examples are with SwiftUI but I am not using SwiftUI.
Edit: After removing collect from my isValidForm function call I am able to get the response but now it is coming multiple times. As a result of that my api call is executing multiple time.
Upvotes: 0
Views: 1331
Reputation: 23701
There is a working example of how one might use this code below.
isValidForm()
now just returns the publisher, it doesn't take in values. The values you are trying to validate are coming in through the four streams that you've constructed.
As I suggested, I've included .print
operators so that you can see, in the debug output, how each stream participates in the validation.
In the main body of the playground I send values into the four streams one at a time. As each value is sent, the isValidForm()
publisher emits a false
value until all the fields have been sent. It then emits a true
letting you know that all four fields have valid values.
import UIKit
import Combine
class Form : ObservableObject {
@Published public var firstName = ""
@Published public var lastName = ""
@Published public var phoneNumber = ""
@Published public var emailAddress = ""
var isFirstNameValidPublisher: AnyPublisher<Bool, Never> {
$firstName
.map { name in
return name.count >= 3
}
.eraseToAnyPublisher()
}
var isLastNameValidPublisher: AnyPublisher<Bool, Never> {
$lastName
.map { name in
return name.count >= 3
}
.eraseToAnyPublisher()
}
var isUserEmailValidPublisher: AnyPublisher<Bool, Never> {
$emailAddress
.map { email in
let emailPredicate = NSPredicate(format:"SELF MATCHES %@", "[A-Z0-9a-z._%+-]+@[A-Za-z0-9.-]+\\.[A-Za-z]{2,64}")
return emailPredicate.evaluate(with: email)
}
.eraseToAnyPublisher()
}
var isPhoneNumberValidPublisher: AnyPublisher<Bool, Never> {
$phoneNumber
.map { phoneNumber in
return phoneNumber.count >= 8
}
.eraseToAnyPublisher()
}
public func isValidForm() -> AnyPublisher<Bool, Never> {
return Publishers.CombineLatest4(
isFirstNameValidPublisher.print("first name"),
isLastNameValidPublisher.print("last name"),
isUserEmailValidPublisher.print("email"),
isPhoneNumberValidPublisher.print("phone"))
.map { isFirstNameValid, isLastNameValid, isPhoneNumberValid, isEmailValid in
return isFirstNameValid && isLastNameValid && isPhoneNumberValid && isEmailValid
}
.print("isValidForm")
.eraseToAnyPublisher()
}
}
let form = Form()
let validWatcher = form.isValidForm().sink { isValid in
print("Is the form valid?: \(isValid)")
}
form.firstName = "Scott"
form.lastName = "Thompson"
form.phoneNumber = "(123) 555-1234}"
form.emailAddress = "[email protected]"
Upvotes: 2