iOS App Architecture: MVVM-C at Scale

Nasir Mahmood
3 min readApr 3, 2021

Code Sample: Here

During last one year there was a big task at hand, how to modularise application with an architecture that have following considerations.

  • SOLID should be practised.
  • Code should be based on an architecture with less boilerplate code, thus an unofficial industry standard calledMVVM-C was obvious choice.
  • Every layer should be interfaced via dependency management and lays foundation for high level DI-Framework.
  • Code should be easilytestable and mocks can be easily replaced with original objects.
  • Project should be structured to scale, as big as it can be.
  • Folder structure should be highlighting the architecture.
  • Shared areas and team based divisions should be implemented as independent Modules (Swift Package)
  • Layers should be interfaced together via Protocols.
  • Architecture should be close to Android so that we can resolve problems at once and use on multiple platforms.
  • Code should be following iOS Swift Style Guidelines and have similar styling throughout the code.
  • Code should be easily extendable, maintainable and understandable.

After discussions at length we came to a conclusion to bench mark the following architecture.

Architecture Reference

Unmarked arrows show ownership.

Its a MVVM-C architecture with few more layers to cater special needs. Layers are defined here.

Coordinator

Application Screen-flow and Push Routing handling should be done in this layer.

Model

Model acts as an entity. In sample code model is shared entity between Codable and NSManagedObject, thus needs some boiler-plate, as both APIs do not go along automatically.

View

Display of data can be done with Storyboard or in code. If Component based design system i-e Style Dictionary is under consideration in future, it’s better to go with code based user interfaces i-e UIKit in code or SwiftUI.

Data is being displayed in the form of Models (viewData/ViewModel). Views conform to Model Configurable to populate data via configure(model:) method.

ViewController

Controller contains logic of view’s state representation and pass actions to viewModel and listens for actions from viewModel.

ViewModel

  • ViewModel is only stateful layer and does contains all the business logic, with or without the help of mappers.
  • Also it informs view-controller about the state update via bindings.
  • Bindings can be implemented by any method of your choice closures, delegates, observers, Combine, RxSwift and Promises etc.

Mapper/UseCases (Optional)

Mapper are the Domain Usecases. Any domain logic will be injected in the form of independently tested usecases. Idea is to keep Domain-Layer independent from ViewModels thus can be widely reuseable. Also on higher level there can be an independent module of just mappers/usecases, written in a cross-platform technology i-e Kotlin Native and exported for both iOS and Android. This can set a foundation for shared domain logic between both platforms.

Repository

Its a facade and abstraction layer between ViewModel and Data Stores that encapsulates Data-source layer. Currently there are two data sources, Network and CoreData in example. This layer can also encapsulate further data sources i-e Firebase Remote Configurations etc.

Interactor

This layer interacts with network interface that fetches and decodes data. It’s a wrapper written on top of Swift Package Ceres which fetches and decodes data into inferred model object.

Persister

Persister encapsulates CoreData and fetches and updates data. [Edit] An (optional) abstraction protocol i-e Persister, can be placed between CoreDataService and Repository to make unit testing simpler.

Other

Other data sources can also be part of your repository, i-e Firebase remote config to turn features on/off or for giving arrangement of features in a particular flow etc.

Sample Code:

This architecture reference is backed with a sample project available here. Please be critical and leave your comments here or create issues on Github.

Conclusion

There are tons of ways to create an application architecture, as there are no rights or wrongs, at the same time there are tradeoffs that can be expensive. Keeping expense to bare minimum and robustness as high as possible, it is an effort to introduce iOS app to modern modular architecture.

--

--

Nasir Mahmood

Staff Software Developer and Entrepreneur. Make apps professionally and also for fun.