iOS: RxSwift + clean architecture
In a previous post we had the opportunity to define the architecture of our app conceptually. The purpose of this post is to get deep into implementation of key components of this architecture we’re using.
In this post we’ll talk about how we implement:
RxSwift + clean architecture
Our app uses ReactiveX swift version: RxSwift. RxSwift is a powerful tool to compose asynchronous operation and event/data streams. These streams make easy the communication between layers. That’s why we think that it fits perfectly to our architecture demands.
The way RxSwift streams through the different layers of our architecture is represented in this scheme:
Basically, Presenter execute a use case that returns an Observable. This Observable is created on data layer and passed by domain layer. Presenter subscribes to this Observable to fetch the result from the repository.
But, what is an Observable?
If we read the RxSwift’s Getting Started document we can understand what an Observable is:
Every
Observable
sequence is just a sequence. The key advantage for anObservable
vs Swift'sSequence
is that it can also receive elements asynchronously. This is the kernel of RxSwift.
Let’s get into implementation layer by layer:
Implementation
- UI Layer
Presenter has a use case to execute. The method execute
returns an Observable. Presenter subscribes to this Observable:
Notice that after the subscribe closure, we call disposed(by: disposeBag)
. This method adds the subscription to the disposeBag
in order to cancel that subscription. Using [unowned self]
or [weak self]
inside of the closures and dispose(by:)
method at the end will save your app from a memory leak. 😉
- Domain layer
The use case has a method execute
that invokes a CatalogRepository
protocol method and returns an Observable created by that repository:
- Data layer
Data repository implements CatalogRepository
protocol defined in domain layer and has and array of data sources. Those data sources, in turn, implements CatalogDataSource
protocol. Inside each method from data repository, it invokes a function from CatalogDataSource
for each data source that it contains. To concatenate those invocations we use the Observable’s operation concat as follows:
Remote data source creates an Observable and execute the request using Alamofire. The response result will be sent to presenter by Observable onNext()
, onCompleted()
or onError()
methods.
And that’s all! Connection through layers done! ✅
This could be a common case in everyone’s app, but RxSwift are a powerful tool, this is just the tip of the iceberg.
Let’s get deep into one of the things we use more in our projects:
Operators
There’re numerous operators in RxSwift: to create, to transform, to filter, to combine, … that can make your life easier. Here you can find a list. In case you need an operator and don’t know how to find it there is a decision tree of operators. There’re also a marble diagram to see how operators work: RxMarbles.
In this post, we’ve already seen concat operation, but we are going into detail about other operators we use the most.
- zip
We use this operator to get the result of multiple Observables and operate with their results.
- flatMap
This operator saved our lives. 🙏 We use this operator when we need to concatenate multiple Obsevables, but the result of the previous Observable will be needed for the execution of next Observable.
- just / error
We use these operators to return an specific observable type as required. In the following example we use both.
- from / filter / sorted / map / take / toArray
We use all these operators when we fetch data from Realm. In the following example we can see how powerful Observables’ operators are when combined. ✨
You can also create custom operators.
Hope you found this post interesting and useful for your projects. Any question or comment will be welcome!
Thanks and good luck!!!
Related articles
In the following posts we detail other key components from our architecture: