I'm still a beginner in using Combine.
Is there a better approach in seeing text changes over two text fields at the same time using Combine?
Unfortunately, it is hard to find what would be the best practice when using Combine in UIKit apps, as, you may know, there are no simple ways to integrate Combine and UIKit. So, every developer is a beginner.
But one thing critically bad in your example is that you create a new instance of LoginViewModel at each time Notification is received.
Other than that, I cannot say which is the better as there are very few examples on the web.
The following is just that I would write something like this in cases you described:
ViewModel
import UIKit
import Combine
class LoginViewModel {
//↓Use _plural_ form
var cancellables = Set<AnyCancellable>()
init() {
$myUsername
.removeDuplicates()
.combineLatest($myPassword.removeDuplicates())
.sink(receiveValue: {username, password in
self.validateUser(username: username, password: password)
})
.store(in: &cancellables)
}
convenience init(username: String, password: String) {
self.init()
myUsername = username
myPassword = password
}
@Published var myUsername: String?
@Published var myPassword: String?
func validateUser(username: String?, password: String?) {
print("\(username ?? "")")
print("\(password ?? "")")
//If you need to update some UI, add another publisher to which the ViewController can subscribe to.
//...
}
}
import UIKit
extension LoginViewModel {
func bind(_ textField: UITextField, to property: ReferenceWritableKeyPath<LoginViewModel, String?>) {
NotificationCenter.default.publisher(for: UITextField.textDidChangeNotification, object: textField)
.sink(receiveValue: { result in
if let textField = result.object as? UITextField {
self[keyPath: property] = textField.text
}
})
.store(in: &cancellables)
}
}
ViewController
import UIKit
import Combine
class HomeViewController: UIViewController {
// MARK: - Variables
let viewModel = LoginViewModel()
// MARK: - IBOutlet
@IBOutlet var usernameTextField: UITextField!
@IBOutlet var passwordTextField: UITextField!
// MARK: - Life cycle
override func viewDidLoad() {
super.viewDidLoad()
viewModel.bind(usernameTextField, to: \.myUsername)
viewModel.bind(passwordTextField, to: \.myPassword)
}
}