RxSwift with MVVM

You have heard RxSwift, you also have heard MVVM pattern, but you might not combine them together. Today I will write a simple example to deal with this, before you want to continue to read this, I recommend you must have some basic knowledge about Reactive Programming and Automatic Reference Counting first.

First, let’s talk a little bit about MVVM.

MVVM stands for Model-View-ViewModel, it’s a slightly different implementation of Apple’s poster-child MVC (Model-View-Controller). Let’s look two below pictures and see some differences between them.

MVC
MVVM

ViewModel takes a central role in the architecture: It takes care of the business logic and talks to both the model and the view.

MVVM follows these simple rules:

Models don’t talk directly to other classes, although they can emit notifications about data changes.

View Models talk to Models and expose data to the View Controllers.
View Controllers only talk to View Models and Views as they handle viewlifecycle and bind data to UI components.

Views only notify view controllers about events (just as with MVC).

Ok, it seems you have a little grasp on MVVM. I don’t want to dive it much since this post is not targeted about this. Let’s open Xcode and create an example. In this example, I will show a list super star on table view.

First, create a Podfile and write this to pull RxSwift and RxCocoa to you project.

platform :ios, ‘9.0’
target ‘RxSwiftWithMVVM’ do
use_frameworks!
 pod ‘RxSwift’
pod ‘RxCocoa’
end

In project workspace. Project’s hierarchy should look like this:

Each View (aka ViewController, Cell…) will have its ViewModel:

FirstViewController has FirstViewModel, CustomTableViewCell has CustomTableCellViewModel.

But it depends on you, I don’t use CustomTableCellViewModel because I will use didSet instead of using RxSwift

We have a model call SuperStar:

final class SuperStar {
    let name: String
    let club: String
    let avatar: String
    init(name: String, club: String, avatar: String) {
         self.name = name
         self.club = club
         self.avatar = avatar
    }
}

We have a Utilities to create database:

final class Utilities {
    static func createData() -> [SuperStar] {
        let ronaldo = SuperStar(name: “Cristiano Ronaldo”, club: “Real Madrid”, avatar: “ronaldo”)
        let messi = SuperStar(name: “Leonel Messi”, club: “Barcelona”, avatar: “messi”)
        let torres = SuperStar(name: “Fernando Torres”, club: “Atletico Madrid”, avatar: “torres”)
        return [ronaldo, messi, torres]
    }
}

Our CustomTableViewCell has two labels to show super star’s name and club, and one image view to show super star’s avatar:

final class CustomTableViewCell: UITableViewCell {
  static var Identifier = “CustomTableViewCell”
  @IBOutlet weak var avatarImageView: UIImageView!
  @IBOutlet weak var nameLabel: UILabel!
  @IBOutlet weak var clubLabel: UILabel!
  var superStar: SuperStar? {
    didSet {
guard let superStar = superStar else { return }
nameLabel.text = superStar.name
clubLabel.text = superStar.club
avatarImageView.image = UIImage(named: "\(superStar.avatar)")
}
   override func awakeFromNib() {
      super.awakeFromNib()
   }
}

Here, I don’t want to use data binding from RxSwift since using didSet is enough.

Next at FirstViewController:

final class FirstViewController: UIViewController {
  @IBOutlet weak var tableView: UITableView!
  let viewModel = FirstViewModel()
  override func viewDidLoad() {
    super.viewDidLoad()
    configureTableView()
    bind()
  }
  private func registerCell() {
    let nib = UINib(nibName: CustomTableViewCell.Identifier, bundle: nil)
    tableView.register(nib, forCellReuseIdentifier: CustomTableViewCell.Identifier)
  }
  private func configureTableView() {
    registerCell()
    tableView.rowHeight = 90
  }
private func bind {
    viewModel.superStars.asObservable()
      .bind(to: tableView.rx.items(cellIdentifier: CustomTableViewCell.Identifier, cellType: CustomTableViewCell.self))                           {
      row, superStar, cell in
        cell.superStar = superStar
      }.addDisposableTo(disposeBag)
   }
}

This do exactly what the View do, excuse some codes about register cell, row height, binding UI.

Let’s go ahead at FirstViewModel:

final class FirstViewModel: NSObject {
 // private let disposeBag = DisposeBag() 
let superStars = Variable<[SuperStar]>([])
 init() { 
superStars.value = Utilities.createData())
 }

Because I don’t want this post longer, so I just load data from local, not from network. Thank for RxCocoa, we can binding our Observable value to Table View Cell.

Only a few of codes and easy to read, right! Build the project and see the result:

We showed super stars successfully on the Table View. But there is one thing before I close this post. Does it make sure to not cause memory leak?

Let’s check it by adding the tracing resource flag in Podfile:

platform :ios, ‘9.0’
target ‘RxSwiftWithMVVM’ do
use_frameworks!
 pod ‘RxSwift’
pod ‘RxCocoa’

post_install do |installer|
installer.pods_project.targets.each do |target|
if target.name == ‘RxSwift’
target.build_configurations.each do |config|
if config.name == ‘Debug’
config.build_settings[‘OTHER_SWIFT_FLAGS’] ||= [‘-D’, ‘TRACE_RESOURCES’]
end
end
end
end
end
end

And add this in AppDelegate to print the resource count every one second:

_ = Observable<Int>
    .interval(1, scheduler: MainScheduler.instance)
        .subscribe(
            onNext: { _ in
                print(“Resource count: \(RxSwift.Resources.total).”)
            }
)

Next step, adding a ViewController before FirstViewController, then from ViewController, we push to FirstViewController, then back again to ViewController, we expect the resource count should be reset again as it was in ViewController, right :)

See the resource count

There is no memory leak here! YAY!!

That’s it! You have learned a basic concept about RxSwift with MVVM. I hope you enjoy this tutorial, thanks for reading and I will see you next time in another post.