Functional programming to the rescue

We will use the map function on the control property to filter out values as follows:

usernameTextField.rx.text.map { $0 ?? "" }

$0 represents the first variable, and if this is nil, we will return an empty string and pass that through to bind the returned value to the ViewModel's variable as desired by making use of the bindTo operator:

usernameTextField.rx.text.map { $0 ?? "" 
.bind(to: loginViewModel.username)

This will simply tell the ViewModel that the input of this text field has changed. The preceding line of code might throw a warning about an unused value.

To silence the warning, add _ =.
This can be resolved as follows:
_ = usernameTextField.rx.text.map { $0 ?? "" }
.bind(to: loginViewModel.username)

That should remove the warning. Similarly, bind the password text field to ViewModel's password variable:

_ = passwordTextField.rx.text.map {$0 ?? "" }
.bind(to: loginViewModel.password)

We can also bind the isValid result to our loginButton so that, if its not valid, we can ensure that the login button is not enabled; we can do this by making use of the library again, as follows:

_ = loginViewModel.isValid.bind(to: loginButton.rx.isEnabled)

This code line will bind the Boolean Observable to the isEnabled property of loginButton.

Finally, we want to control the state of the app by listening to the isValid property of the ViewModel. Let's do it in a slightly different way just to see how we can do this without making use of the bindings:

_ = loginViewModel.isValid.subscribe(onNext: {[unowned self] isValid in
self.validationsLabel.text = isValid ? "Enabled" : "Disabled"
self.validationsLabel.textColor = isValid ? .green : .red
})

Let's segregate and explore the code in the code block—loginViewModel.isValid.subscribe allows you to subscribe to any change in the isValid property's value. Since isValid is declared as an Observable, we can subscribe to the events that isValid Observable will emit. We can subscribe to three different types of event emitted by an Observable: next, error, and completed. Dig into the Event definition, and you will come across the following:

/// Represents a sequence event.
///
/// Sequence grammar:
/// **next* (error | completed)**
public enum Event<Element> {
/// Next element is produced.
case next(Element)
/// Sequence terminated with an error.
case error(Error)
/// Sequence completed successfully.
case completed
}

Don't worry if you can't fully comprehend the preceding code. We will cover all the topics touched on in the code that we wrote in this chapter in full detail in upcoming chapters. This chapter aims to give the reader a feel for how convenient and clear your code can look (and work) if you use RxSwift concepts.

Coming back to subscribe() as used in the preceding code:

loginViewModel.isValid.subscribe(onNext: { isValid in
})

We are listening only for next events emitted by isValid, and this subscription will pick up anything that is emitted by the isValid Observable. Inside the closure, we want to react to the events emitted by the isValid Observable. As per our validation logic, we want to make the login button enabled or disabled, depending upon the number of characters inside the username and password text fields. Let's start by making the validationLabel react to emitted events:

loginViewModel.isValid.subscribe(onNext: {[unowned self] isValid in
self.validationsLabel.text = isValid ? "Enabled" : "Disabled"
self.validationsLabel.textColor = isValid ? .green : .red
})

If isValid returns true, then validationsLabel will display enabled; if isValid returns false, then validationsLabel will display disabled. Similarly, the color of the label will change depending on the boolean value of the isValid variable.

Run the app on a device or simulator and play with the app; you will notice the desired behavior.

We can now extend the validations to involve the Login button as well. Let's modify the requirements a bit. Let's keep the Login button disabled and inactive until the user enters four or more letters in each text field; if the validations fail, the view should look like this:

Once validations pass, the view should transform to enable the Login button and validationsLabel will say enabled in green , as follows:

We can achieve the aforementioned behavior by making small changes to the code written in the viewDidLoad() method of the ViewController. Firstly, we have to disable the Login button and change the background color in viewDidLoad():

loginButton.isEnabled = false
loginButton.backgroundColor = UIColor.lightGray

We can avoid the preceding step if we keep the default state of the login button at disabled. Now we want to change the state of the login button as soon as the validation rule returns true, that is, the isValid variable of ViewModel returns true, and we can achieve this by refactoring the subscription as follows:

_ = loginViewModel.isValid.subscribe(onNext: {[unowned self] isValid in


self.validationsLabel.text = isValid ? "Enabled" :
"Disabled"
self.validationsLabel.textColor = isValid ? .green : .red


//Making changes to the login button
self.loginButton.isEnabled = isValid ? true : false
self.loginButton.backgroundColor = isValid ? UIColor.orange
:UIColor.lightGray
})

We are changing the state of the Login button depending on the value of the isValid property. Right now, we are handling all the events emitted by the isValid observer, and we can validate by logging in values returned by isValue in the subscription code:

_ = loginViewModel.isValid.subscribe(onNext: {[unowned self] isValid in


self.validationsLabel.text = isValid ? "Enabled" : "Disabled"
self.validationsLabel.textColor = isValid ? .green : .red


//Log the values with each key in event
print(isValid)


//Making changes to the login button
self.loginButton.isEnabled = isValid ? true : false
self.loginButton.backgroundColor = isValid ? UIColor.orange :
UIColor.lightGray
})

Build and run the app once again and note the value of the isValid  variable in the console. Finally, the ViewController file will look like the following:

//  ViewController.swift
// RxSwiftLoginView
//
// Created by Navdeep on 27/9/17.
// Copyright © 2017 Navdeep. All rights reserved.
//
import UIKit
import RxSwift
import RxCocoa
class ViewController: UIViewController {
@IBOutlet weak var usernameTextField: UITextField!
@IBOutlet weak var passwordTextField: UITextField!
@IBOutlet weak var loginButton: UIButton!
@IBOutlet weak var validationsLabel: UILabel!


var loginViewModel = LoginViewModel()


override func viewDidLoad() {
super.viewDidLoad()
loginButton.isEnabled = false
loginButton.backgroundColor = UIColor.lightGray

//Bind UI textFields to properties in ViewModel


_ = usernameTextField.rx.text.map { $0 ?? "" }
.bind(to: loginViewModel.username)
_ = passwordTextField.rx.text.map {$0 ?? "" }
.bind(to: loginViewModel.password)


_ = loginViewModel.isValid.bind(to: loginButton.rx.isEnabled)


_ = loginViewModel.isValid.subscribe(onNext: {[unowned self]
isValid in


self.validationsLabel.text = isValid ? "Enabled" :
"Disabled"
self.validationsLabel.textColor = isValid ? .green : .red


//Log the values with each key in event
print(isValid)

//Making changes to the login button
self.loginButton.isEnabled = isValid ? true : false
self.loginButton.backgroundColor = isValid ? UIColor.orange
:UIColor.lightGray
})
}
}
..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset