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

Photo by Yu Hai / Unsplash

This tutorial will use all pieces of information we gathered from previous posts to refactor existing code to RxSwift. I created a base project. Simple iOS application displaying images in the collection view. Our "API" is in a separate framework to mimic app modularization. The starter is done without any reactive code, old-school callbacks, and code blocks we used to love;)

Part 1 will make sure image API is reactive compatible. In the second part, we will refactor the collection view to use reactive code and we will simplify the view controller and our collection cell.

Build and run the application, you should see a grid of "images" from placeholder API.

Adding reactive extension

Before we start, we need RxSwift. Add swift package with RxSwift to ApiClient in Xcode. You can follow directions from GitHub. If you want, use CocoaPods or Carthage.

We will make our legacy API reactive ready! To do that add new swift file to the ApiClient project. We want to have an easy and familiar way to use the new Rx extension for API. The best is to have the .rx property available. To do that we need to add one protocol conformance to ImageApi class:

import RxSwift
import SampleApi

extension ImageApi: ReactiveCompatible { }

This is it! Now every  ImageApi instance has .rx property. But it is empty. Let's add downloading images to the reactive extension below:

// 1
extension Reactive where Base: ImageApi {
    // 2
    var getImages: Single<[Image]> {
        // 3
        Single.create { single in
            // 4
            base.getImages { images, error in
                if let error = error {
                    single(.failure(error))
                    return
                }
                guard let images = images else {
                    single(.failure(APIError.noData))
                    return
                }
                single(.success(images))
            }
            // 5
            return Disposables.create { }
        }
    }
}
  1. With the power of extension with generic where clause, I'm adding a new variable only to ImageApi class, we can do that because ImageApi was declared ReactiveCompatible.
  2. Now, instead of method, I'll use the computed variable. I'm using single here because I know this call should just fetch images and then complete. There is no need for more events. Single ensures this.
  3. We are using the .create { } factory method. It passes a function that accepts Result. Our code can use this function to report value or error.
  4. base will return an instance of self - let's call the actual fetching method. Inside callback code will report errors or successes with an array of images.
  5. We need to return disposable here - we do nothing on dispose, so I left it empty.

This is all code we need to add reactivity to our "legacy" images API.

Refactor

Now we can use it. In ViewController change couple of lines of code:

import RxSwift // Add import
class ViewController: UIViewController {
    let disposeBag = DisposeBag() // add dispose bag
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        ...
        // replace .getImages {} with rx counterpart:
        api.rx.getImages
            .observe(on: MainScheduler.instance)
            .subscribe(onSuccess: { [unowned self] in
                self.images = $0
            })
            .disposed(by: disposeBag)
    }
...
// rest of class

I'm adding RxSwift import and instead of using a callback, we use the .rx extension. Instead of nested blocks of code for dispatching images assignment  we have it by adding simple .observe(on: MainScheduler.instance).

And this is it for this time. Next time, with the power of RxCocoa, I'll refactor UI handling to be reactive! Stay tuned!

All changes can be found here.

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