Skip to content

techpro-studio/iOSAppArchitecture

master
Switch branches/tags
Code

Latest commit

 

Git stats

Files

Permalink
Failed to load latest commit information.
Type
Name
Latest commit message
Commit time
 
 
 
 
 
 
 
 
 
 

iOSAppArchitecture

iOS app architecture we use.

MVPCF = Model View Presenter Coordinator Factory;

It is similar to the combination of Reactive MVP + Coordinator + Factory. Presenter is the same thing as ViewModel in MVVM but it has a different name. ViewModel name is used for a different approach;

SOLID principles are the core of our architecture.

For our architecture, we created a pretty simple and small library RCKit aka Reactive Clean Kit, that contains everything we need for building an app using this architecture.

1. For assembling project we use DI pattern with Swinject library.

App has shared Container where we register all shared dependencies we need to use across the whole app.

2. Project is divided up into Flows

Every flow has its own folder. Every flow has its "manager" called Coordinator. Every flow has its own DI Container that contains specific dependencies for the flow.

This object is responsible for handling navigation through the application, handling push notifications, and other routing actions.

Coordinator uses one container viewcontroller like UINavigationController or UISplitViewController.

Coordinator builds the screens (Modules) and controls the flow.

The only exception is a main ApplicationCoordinator that accepts UIWindow instead of container ViewController.

3. Every Module for example has the following structure:

Factory

Abstraction for creation anything. Factory is very important part of this architecture, because it injects dependencies. BaseFactory contains Container we use to inject dependencies that we need. Factory can be used for Modules as well as building Cells, NSOperations, etc where we need to inject something. We can inject factories where we need and it gives us a lot of flexibility!

  protocol <Name>Factory {
    func make()-> <Name>Routes
  } 

Routes

Abstraction for Routes of the Module. ViewController implements this protocol. Closures fire in Coordinator and Coordinator performs what is needed.

   protocol <Name>Routes: ModuleRoutes {
     var canceled: (()->Void)! { get set }
     var finished: ((String)->Void)! { get set }
   }

Presenter

Abstraction for Presenter. It has default implementation. It contains a logic of the presentation. It has the bindings for input and output. Presenter contains structures called ViewModel as well as plain types, ready to use in View.

// for example
struct <SomeCell>ViewModel {
   let name: String 
   let dateFormatter: String 
}

 protocol <Name>Presenter {
   var searchInput: BehaviorRelay<String?> { get }
   var searchResults: BehaviorRelay<[<SomeCell>ViewModel]> { get }
   var nextButtonAvailable: BehaviorRelay<Bool> { get }
 }

View

Abstraction for view. It has default implementation inherited from UIView.

It contains custom view for ViewController.

  protocol <Name>View: ViewContainer {
    var addButton: UIButton { get }
    var textField: UITextField { get }
  }

ViewController

Inherired from UIViewController Implements Routes protocol.

It is responsible for:

  • binding Presenter to View;
  • registering for events as a button tap;
  • firing Routes closures. Basically it is the main building structure of screens.

4. Every Presenter's implementation injects dependencies it needs.

class Default<Name>Presenter: <Name>Presenter{
  
  let name: BehaviorRelay<String?> = BehaviorRelay(value: nil)
  
  let addButtonAvailable: BehaviorRelay<Bool> = BehaviorRelay(value: false)
  
  private var creating = BehaviorRelay(value: false)
  private var disposable: Disposable?
  private let disposeBag = DisposeBag()
  private let modelManager: ModelManager
  private let reachabilityManager: ReachabilityManager
  
  // injecting via initializer
  init(modelManager: ModelManager, reachabilityManager: ReachabilityManager){
      self.modelManager = modelManager
      self.reachabilityManager = reachabilityManager
  }
}

Every dependency should have only one responsibility!

For example:

protocol ReachabilityManager {
  var connectionIsReachable: BehaviorRelay<Bool> { get }
}

Or this:

protocol FirstLaunchManager {
  func performTasksOnFirstLaunch()
}

Or this:

protocol LogoutManager {
  func logout()
}

5. Differences from the other architectures.

  • MVVM and MVP. MVPCF uses MVVM and MVP as a base architecture and improves both with coordinators and factories;

  • VIPER. It has a Presenter as the main Object of the module. Router is not Obvious. MVPCF has ViewController as the main object of the module and Coordinator that handles the navigation. Also, standard VIPER is not reactive from the box.

  • RIBs. Interactor in RIBs is the main object of module, but MVPCF has ViewController as the main object. Our approach is more obvious for typical developers.

About

iOS app architecture we use. Feel free to contribute.

Topics

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages