Getting Started with RxSwift and RxCocoa

Göktuğ Gümüş
13 min readJan 20, 2020

--

Today I’d like to talk to you about a tool that I enjoy using the most and that I believe made me a better developer in my 5 years of iOS developer life. I’d like to point out that we used RxSwift intensely in my last two jobs. Therefore, I wanted to prepare an article for people who want to start in reactive programming.

The original article is written in Turkish. You can check it out.

What are RxSwift and RxCocoa?

RxSwift library allows us to use Swift disparately. With this library, asynchronous programming becomes easier to do and more legible. It allows you to build more solid architectures and applications with higher quality.

RxCocoa library allows us to use Cocoa APIs used in iOS and OS X with reactive technics.

Observables and Observers

One of the concepts you’ve got to know in this article is Observable and the other is Observer.

  • Observable means the constructs that bring out the changes.
  • Observer constructs are the ones that subscribe to Observable constructs and are informed when there’s a change.

DisposeBag

RxSwift and RxCocoa contain a tool named DisposeBag which helps ARC and memory management. We can think DisposeBag as a virtual bag that carries Observer objects. We can use DisposeBag tool to properly dispose of Observers when the parent objects, which we define Observers with, are deallocated.

You may feel a little confused about the terms but you’ll understand them better later on in the article.

Let’s Start

I prepared a little application whose reactive technics aren’t being used to be able to explain RxSwift and RxCocoa libraries to you better. We will turn the application into a reactive one step by step later on in the article. CoffeeShop application that I prepared contains user homepage, menu page where you can see all the coffees in the shop, detail page where you can order coffees and cart page where you can see the coffees you want to buy. I specifically suggest that you download the initial state of the project and do all the changes step by step with me.

You can clone the starter and final version of the project using git clone https://github.com/Goktug/RxSwift-RxCocoa-CoffeeShop-Medium.git command in the terminal.

First, you need to download the project and extract it from the zip file. Then, you need to run pod install command to install RxSwift and RxCocoa Pods. After the Pod has been installed, you need to open CoffeeShop.xcworkspace file with Xcode.

General Overview

Homepage

There’s a homepage where we ask the user for their e-mail and password information. We are not going to use a real authentication mechanism that communicates with the backend server here. We are only going to look at the requirement of the e-mail being in the right format and the password being at least 6 characters. If the e-mail and the password meet with the requirements we want, we are going to direct the user to the menu page.

Homepage

When we open LoginViewController.swift file, we see the E-mail, Password texfield and Login button that are connected with IBOutlet. In addition to this, logInButtonPressed() function calls when they press the login button. As you can see, there’s no validation mechanism for the text fields at the moment. Let’s add a validation mechanism to this page using reactive programming principles.

First, we need to import RxSwift and RxCocoa libraries to the top of the file.

import RxSwift
import RxCocoa
class LoginViewController: UIViewController { ... }

We can receive the text input entries of the text field instantly in a reactive way thanks to RxSwift and RxCocoa. Thus, we will control the validness of the e-mail and password area every time the user enters input. If both of the inputs meet our requirements, we will activate the Log In button. In non-reactive programming, we needed to implement the methods of the UITextFieldDelegate delegate to our class and set a lot of complicated if/else conditions. Thanks to reactive programming, we can directly reach these inputs.

Let’s add the code below to the LoginViewController class.

private let disposeBag = DisposeBag()

Like I mentioned before when the instance of our class is deallocated, DisposeBag allows us to properly dispose of its Observers.

Let’s add the e-mail validation function below inside our class. We are going to use that to verify the e-mail input we get from the user.

Now, it’s time for the e-mail validation part. Let’s add the code below to our viewDidLoad method.

Let’s see step by step what these codes do:

  1. Thanks to text, one of the extensions of RxCocoa, we can reach the text parameter of UITextField as an Observable variable.
  2. Normally, the text data of textfield is return as String or nil. orEmpty makes sure the data always comes in String type.
  3. The validness of the input is checked with the validateEmail method. It turns into true if the e-mail is in the right format, false if not. Because we want the emailValid variable to become a Bool type Observable variable, we need to turn the input into Bool type with map through String.
  4. The debug function allows us to see the course of events on the console. You don’t have to add this, it’s optional. I recommend you to add to be able to observe the process better at the beginning.
  5. If there are other Observers subscribing to this Observable variable, it’s ensured thanks to share this process isn’t repeated.

Now, this code runs each time the user typed something. We can optimize it by getting the inputs not each time the user typed something. The throttle or debounce filtering operators that RxSwift offers are ideally suited for this job. We are going to use the throttle operator since it’s more suitable for us.

.throttle(0.1, scheduler: MainScheduler.instance)

Let’s take this code as an example. Thanks to this code, it waits for another input to come for 0.1 second after each time an input comes. If an input comes within this period, it waits another 0.1 second for another input to come. If it doesn’t get another input in 0.1 second after the last input has been taken, it gives the final state of the input to flow. And this prevents our application to run unnecessary codes in fast input entries and the lockouts that might arise because of that.

Throttle flow diagram

Let’s integrate the throttle mechanism to our code. Let’s add the code blow to the top of the class.

private let throttleInterval = 0.1

And let’s change the code below with the one we wrote before.

You need to do the same things to control the password input. Let’s add the code below under the code we wrote above.

Let’s take a look at the different code since it’s exactly the same with the codes we wrote for e-mail except for the map code:

  1. We write this requirement into our map function since we only have the requirement of at least 6 character in the password input. We give the String type input to the flow by turning it into Bool type.

Now, we need to create an Observable variable that allows us to check the validness of our inputs at the same time.

CombineLatest is one of the concatenation operators of RxSwift. We can gather multiple same-typed Observable variables under one Observable variable thanks to this method.

combineLatest flow diagram

We want our everythingValid variable to be true when we combine the emailValid and passwordValid variables with the && operator and If both are true, It will return true. Otherwise, it will return false. We will connect our Observable variable to the isEnabled parameter of the button named logInButton. And that will allow the login button to be active when the e-mail and password inputs are true and when clicked, for them to go to menu page.

Let’s add the code below to the bottom of the viewDidLoad method.

Let’s take a look at what these codes do step by step.

  1. bind(to:) function allows us to connect the Observable variable with the same type. (It turns the isEnabled variable of the rx.isEnabled button, one of the extensions of RxCocoa, to an Observable variable.)
  2. bind(to:) function returns a Disposable typed variable. We need to add this to disposeBag of this class to dispose of the Observer we created when the LoginViewController class deallocated.

We have completed the functionality of our homepage now. We can move on to our next page.

Menu Page

We can view the coffee types in the shop in this page. We can go to the detail page of the coffee we want to buy by clicking on it. We can also go to the Cart page by clicking on the cup icon on the top right side of the page. We can see the coffee number that we added in the little red circle on top of the icon.

Menu Page

Now firstly, we’ll make our UITableView UI component reactive using RxCocoa. RxCocoa has a number of reactive APIs for UITableView. This way, we won’t have to override the UITableViewDataSource and UITableViewDelegate delegates. RxCocoa will do that for us.

Firstly, open the MenuViewController.swift file and delete the UITableViewDataSource and UITableViewDelegate extension codes that are at the bottom of the file.

Then, delete codes below from the configureTableView() function. You won’t need these codes anymore.

tableView.delegate = self
tableView.dataSource = self

But since we deleted UITableViewDelegate delegate, we need the add the code that specifies the height of the cells inside the configureTableView function. Thus, we need to add the code below.

tableView.rowHeight = 104

Now to connect the coffee data that will be in the menu to the table view reactively. In order for table view to be connected to these data reactively, our data construct needs to be reactive as well.

To ensure that, we need to make the coffees variable Observable. We need to update the code as stated below.

.just(_:) function shows that this Observable type variable will never vary. But even if our variable is invariable, we would be creating an Observable variable.

Note: Using the .just(_:) method knowing the data will never change might seem meaningless but we need this to properly use both reactive programming technics and the power of Rx.

Add the code below to the top of the MenuViewController class.

private let disposeBag = DisposeBag()

Add the code below to the viewDidLoad method after configureTableView() .

This code makes sure that the cells are added to table view should there be a change in the coffees variable. In our case, 5 coffee data that we defined to coffees variable will be added to table view as cells.

Let’s see what these codes do step by step.

  1. We call the bind(to:) function to link this code which will run for every line in table view with the coffees variable.
  2. We call the rx method to reach RxCocoa extensions.
  3. We need to pass the cell identifier and the class of the cell that we want to use in the items(cellIdentifier:cellType:) method. This way, Rx framework calls the dequeuing methods that we normally created using delegate on its own.
  4. This code block runs for each new element. It allows us to get row, element and cell information. In our case, the element variable is Coffee type. We configure our cells consisting of CoffeeCell by giving configure(with:_) method the current coffee data.
  5. bind(to:) function returns a Disposable type variable. We need to add it to the disposeBag of this class to properly dispose of the Observer we created when the MenuViewController class deallocated.

In addition to this, Rx Framework will calculate the outputs of the tableView(_:numberOfRowsInSection:) and numberOfSections(in:) methods according to the observed variable automatically. Also, as you can see, we changed the tableView(_:cellForRowAt:) method with the closure we wrote above.

Now, let’s run the application and check if everything is as they used to be. Not bad. Almost everything is as it was before but there’s something missing. The application doesn’t know what to do now since we deleted the tableView(_:didSelectRowAt:) method.

To fix this, we need to use the modelSelected(_:) method, another extension of RxCocoa for table view. This method returns the model of the selected (clicked) cell as Observable.

Let’s add the code below under the code we added above.

Let’s see what these codes do step by step:

  1. We call the rx method to access RxCocoa extensions.
  2. This passes the type of the element that is going to return to the modelSelected(_:) method, reactive extension of table view, and returns the element selected from the table view as Observable.
  3. We pass this Observable variable to the closure inside of the subscribe(onNext:) method. This closure runs when each cell is selected (clicked.)
  4. We run the segue that we previously defined from Storyboard to go to the coffee detail page.
  5. We remove the line we selected from the table view from selected state.
  6. The subscribe(onNext:) method returns a Disposable type variable. We need to add it to the disposeBag of this class to properly dispose of the Observer we created when the MenuViewController class deallocated.

Finally, our job is done with table view. Now, our table view is running completely reactively.

Let’s Make the Cart Button In the Navbar Reactive Too!

The number inside of the red circle in our button updates itself according to the product quantity in the cart. In our current construct, there’s a code particle that takes the total order count from ShoppingCart model and updates the badge in the button in the viewWillAppear(:_) method of MenuViewController. If we change the coffees variable in our ShoppingCart model to an Observable variable, we won’t always have to take the final state of the order count in the viewWillAppear(:_) method. Thanks to reactive programming, we can ensure that the badge in our button is automatically updated every time a new element comes into the coffees variable.

Let’s open the ShoppingCart.swift file and change it with the code below.

Let’s take a look at what these codes do step by step:

BehaviourSubject Diagram
  1. Here, for the first time, we use a type other than Observable. BehaviourRelay is a wrapper of BehaviourSubject. We can say that Subjects are proxies or bridges which can act as both Observable and Observer in the Rx concept. I will explain further these concepts in another article. But for now, what we need to know is: BehaviourSubject returns us the element it last emitted when we subscribe to a variable.
  2. When BehaviourSubject accesses the value parameter of a variable, we can get the element that’s been emitted last. coffees.value code returns us a [Coffee: Int] type dictionary. We will assign this dictionary to a temporary variable and update the coffee that will be added.
  3. We can emit a new element inside a BehaviourSubject type variable with the accept(:_) method.
  4. Here, we take the last value inside the variable same as when we added, and assign it to the variable. Then, we remove the coffee we want to delete from the dictionary.
  5. We emit the dictionary we updated to thecoffees variable.
  6. We turn the getTotalCost() method into a method that returns Observable<Float> instead of Float.
  7. We mentioned that BehaviourRelay acts as both Observable and Observer. We need to take the price information of the elements inside the coffees variable and return their total. At the same time, we need to return it as an Observable variable. To do that, we can access the elements inside the coffees variable by mapping it and change it however we want.
  8. We turn the getTotalCount() method to a method that returns Observable<Int> instead of Int.
  9. We return the total amount of coffee in the cart as Observable.
  10. We turn the getCartItems() method to a method that returns Observable<[CartItem]> instead of [CartItem].
  11. We return the CartItem model that we use in the table view at the basket page as an Observable array.

Our ShoppingCart model is now reactive. However, if you want to run your project now, you will see that Xcode gives error in several places. You need to return the places where we read data from the ShoppingCart model reactive as well in order to run your project successfully.

Let’s go back to MenuViewController.swift file and delete the viewWillAppear(:_) method completely.

Paste the code below to the bottom of the viewDidLoad method.

Here, we subscribe to the getTotalCount() named Observable<Int> type method of our ShoppingCart model. Now, whenever there’s a change in the coffees variable, current sum information of the ordered coffees returns with totalOrderCount variable.

Cart Page

This page contains the number and prices of the coffees that we select from the menu and add to our cart. There’s also the option to delete the wanted order from the cart. You can see the total amount to be paid at the bottom of the page as well. Whenever you delete an order, the amount updates itself.

Cart Page

Let’s open the ShoppingCartViewController.swift file. We need to make the table view reactive here as well. I’m not going to repeat the things we learned on the menu page. I will only talk about how we can make the row/cell deletion, which we are going to learn now, reactively.

Let’s start with changing the code below with the current code.

Let’s see what these codes do step by step.

  1. When we pass and call the element that will return to the modelDeleted(_:) method, which is a reactive extension of table view, the method will return us the element that’s been deleted from table view as Observable.
  2. We pass this Observable variable to the closure inside the subscribe(onNext:) method.
  3. We delete the coffee object in our CartItem model that’s been deleted from our ShoppingCart model as well.
  4. The subscribe(onNext:) method returns us a Disposable variable. We need to add this to disposeBag of this class to dispose of the Observer we created when the ShoppingCartViewController class deallocated.

Now, we have made almost all of our projects reactive. There’s only OrderCoffeeViewController.swift file left. If you want to reinforce what we learned here, I’m giving you a homework: Make this page reactive.

If you like the article, please don’t forget to like and follow me.

You can reach me via gktggumus@gmail.com should you have any questions about RxSwift and RxCocoa.

https://www.linkedin.com/in/goktuggumus/

--

--