SherifKamalSalem / Chefaa

Chefaa Senior iOS Engineer position's task using Clean Architecture Concepts with MVVM and RxSwift

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool


Clean architecture Concepts with MVVM and RxSwift


Dependencies in this project are provided via Cocoapods. Please install all dependecies with

pod install

High level overview


The Domain is basically what is Chefaa's App about and what it can do (Entities, UseCase etc.) It does not depend on UIKit or any persistence framework, and it doesn't have implementations apart from entities


The Platform is a concrete implementation of the Domain in a specific platform like iOS. It does hide all implementation details. For example Database implementation whether it is CoreData, Realm, SQLite etc.


Application is responsible for delivering information to the user and handling user input. It can be implemented with any delivery pattern e.g (MVVM, MVC, MVP). This is the place for your UIViews and UIViewControllers. As you will see from the example app, ViewControllers are completely independent of the Platform. The only responsibility of a view controller is to "bind" the UI to the Domain to make things happen. In fact, in the current example we are using the same view controller for Realm and CoreData.

Detail overview

To enforce modularity, Domain, Platform and Application are separate targets in the App, which allows us to take advantage of the internal access layer in Swift to prevent exposing of types that we don't want to expose.


Entities are implemented as Swift value types

public struct Item {
    public let id: Int
    public let title: String
    public let images: [String]
    public let price: Double

UseCases are protocols which do one specific thing:

public protocol AdvertisementsUseCase {
    func homeComponents() -> Observable<HomeComponentsTypeAlias>

UseCaseProvider is a service locator. In the current app, it helps to hide the concrete implementation of use cases.


The Platform also contains concrete implementations of your use cases, repositories or any services that are defined in the Domain.

final class AdvertisementsUseCase: Domain.AdvertisementsUseCase {
    private let network: HomeNetwork

    init(network: HomeNetwork) { = network
    func homeComponents() -> Observable<(sliders: [SliderAdvertisement], subCategories: [SubCategory], brands: [Brand], items: [Item], landingPages: [LandingPage])> {
        let homeComponents = network.fetchHome().flatMap { home -> Observable<(sliders: [SliderAdvertisement], subCategories: [SubCategory], brands: [Brand], items: [Item], landingPages: [LandingPage])> in
            guard let brands =,
                let sliders =,
                let subCategories =,
                let items =,
                let landingPages = else {
                    return Observable.of((sliders: [], subCategories: [], brands: [], items: [], landingPages: []))
            return Observable.of((sliders: sliders, subCategories: subCategories, brands: brands, items: items, landingPages: landingPages))
        return homeComponents

As you can see, concrete implementations are internal, because we don't want to expose our dependecies. The only thing that is exposed in the current app from the Platform is a concrete implementation of the UseCaseProvider.

public final class UseCaseProvider: Domain.UseCaseProvider {
    private let networkProvider: NetworkProvider

    public init() {
        networkProvider = NetworkProvider()
    public func makeAdvertisementsUseCase() -> Domain.AdvertisementsUseCase {
        return AdvertisementsUseCase(network: networkProvider.makeHomeNetwork())


In the current example, Application is implemented with the MVVM pattern and heavy use of RxSwift, which makes binding very easy.

Where the ViewModel performs pure transformation of a user Input to the Output

protocol ViewModelType {
    associatedtype Input
    associatedtype Output
    func transform(input: Input) -> Output
final class HomeViewModel: ViewModelType {
    // MARK: - ViewModel Inputs
    struct Input {
        let trigger: Driver<Void>
    // MARK: - ViewModel Outputs
    struct Output {
        let fetching: Driver<Bool>
        let homeAds: Driver<HomeComponentsViewModelsTypeAlias>
        let error: Driver<Error>
    // MARK: - Properties
    private let useCase: AdvertisementsUseCase
    private let navigator: HomeNavigator
    // MARK: - Init
    init(useCase: AdvertisementsUseCase, navigator: HomeNavigator) {
        self.useCase = useCase
        self.navigator = navigator
    // MARK: - ViewModel Transformation
    func transform(input: Input) -> Output {

A ViewModel can be injected into a ViewController via property injection or initializer. In the current app, this is done by Navigator.

protocol HomeNavigator {
    func toHome()

class DefaultHomeNavigator: HomeNavigator {
    // MARK: - Properties
    private let storyBoard: UIStoryboard
    private let navigationController: UINavigationController
    private let services: UseCaseProvider

    // MARK: - Init
    init(services: UseCaseProvider,
         navigationController: UINavigationController,
         storyBoard: UIStoryboard) { = services
        self.navigationController = navigationController
        self.storyBoard = storyBoard
    // MARK: - HomeNavigator Functions
    func toHome() {
        let viewController = storyBoard.instantiateViewController(ofType: HomeViewController.self)
        viewController.viewModel = HomeViewModel(
            useCase: services.makeAdvertisementsUseCase(),
            navigator: self
        navigationController.pushViewController(viewController, animated: true)


class HomeViewController: UIViewController {
    private let disposeBag = DisposeBag()
    var viewModel: HomeViewModel!


The corner stone of Clean Architecture is modularization, as you can hide implementation detail under internal access layer. Further read of this topic here


Any questions?


Chefaa Senior iOS Engineer position's task using Clean Architecture Concepts with MVVM and RxSwift


Language:Swift 95.8%Language:Objective-C 2.8%Language:Ruby 1.4%