RxSwift course - adding RxSwift extensions to existing code - part 2

Photo by Alesia Kazantceva / Unsplash

To finish our conversion from part 1, we will need all changes, available here.

Collection view

Right now we are using old-fashioned collection view implementation, using delegate and data source. I'll simplify this code a lot by using RxSwift and RxCocoa. Let's refactor viewDidLoad:

import RxCocoa


override func viewDidLoad() {
        super.viewDidLoad()
        collectionView.translatesAutoresizingMaskIntoConstraints = false
        collectionView.register(CollectionViewCell.self, forCellWithReuseIdentifier: "cell")
        collectionView.delegate = self
        view.addSubview(collectionView)
        NSLayoutConstraint.activate([
            collectionView.topAnchor.constraint(equalTo: view.topAnchor),
            collectionView.bottomAnchor.constraint(equalTo: view.bottomAnchor),
            collectionView.leadingAnchor.constraint(equalTo: view.leadingAnchor),
            collectionView.trailingAnchor.constraint(equalTo: view.trailingAnchor)
        ])
        api.rx.getImages
            .asObservable()
            .observe(on: MainScheduler.instance)
            .bind(to: collectionView.rx.items(cellIdentifier: "cell", cellType: CollectionViewCell.self)) { (_, element, cell) in
                cell.image = element
         }
         .disposed(by: disposeBag)
    }

As you can see, I'm binding items from API to collections items, this binder takes three parameters, cellIdentifier, cellType, and cell configurator. And with that, we can remove whole extension ViewController: UICollectionViewDataSource, we don't need it anymore!

Collection cell

We can simplify collection cell too. Right now code has to hold a reference to the URL session because we want to cancel this request if the collection is reused before the image loads. We will refactor image variable.

    var disposeBag = DisposeBag()
    
    var image: Image! {
        didSet {
            disposeBag = DisposeBag()
            guard let url = image?.url else { return }
            let request = URLRequest(url: url)
            URLSession.shared.rx.data(request: request)
                .compactMap {
                    UIImage(data: $0)
                }
                .observe(on: MainScheduler.instance)
                .bind(to: imageView.rx.image)
                .disposed(by: disposeBag)
        }
    }

Now, instead of creating and maintaining tasks, I'm using dispose of the bag to cancel the previous request, by creating new bag, the old one is removed from memory, triggering disposing of created observable. After that, I'm creating a new request and execute it using the Rx extension.

Lastly, update reuse method:

override func prepareForReuse() {
    super.prepareForReuse()
    imageView.image = nil
    image = nil
}

And this is it, with Rx we can remove some parts of code, old fashioned data sources and use a more modern, functional style to implement API calls and UI bindings.

Here you can find all RxSwift course posts:

RxSwift course
I’ll put here all RxSwift related posts! RxSwift course - basicsFor 3 years I’m working with RxSwift on daily basis. It helps a lot with data manipulation and UI binding when MVVM is your architecture of choice. The framework gives developers flexibility and extendability. I made even an Rx
Artur Gruchała

Artur Gruchała

I started learning iOS development when Swift was introduced. Since then I've tried Xamarin, Flutter, and React Native. Nothing is better than native code:)
Poland