Master/Detail Clean Architecture Demo App
- Dependency Injection : Every dependencies are defined through abstractions (protocols), so components are lightly coupled and easy to fake while unit testing
- Dependency Inversion : Service abstraction are defined on caller side. The concrete implementation component imports declaration. The abstraction->concrete resolution is made by Abstract Factory pattern
- Interface Segregation : Common reusable services (like dataStore) are accesssed through distincts abstractions depending on what part of the service is really in use
- Single Responsibility : Each Use Case of the app (each screen : master & detail) is decomposed in a chain of decoupled components with very restricted responsibilities, inspired by VIPER Approach (see below)
In Xcode project structure level, this result in many targets which can be built in isolation, greatly accelerating builds and unit tests execution (allowing quick and comfortable Test Driven Development). Furthermore, The folder structure of each target is designed to easily externalizing the component in its own Swift Package (SPM).
VIPER stands for View / Interactor / Presenter / Entity / Router. Each Scene (ie: each use case screen) of the application is designed as collaboration network of 6 types of component.
-
Entities These are Business model objets, holding the business specific logic rules (different from application logic). Entities are designed to be reusable accross multiples apps of the same business.
-
Interactor This is the Use Case (application logic) implementation place. Interactor receives user interaction requests from View, retreives the business data needed to build the user feedback response, gives the data to the presenter.
-
Presenter Play the role of data aggregator between Interactor and View. Interactor gives business (entity related) data objects as input to the presenter. Presenter process and emits simple display data to (mostly String and Enums) View. So that View are not coupled to business or application logic, and so reusable in many use cases.
-
View These are user interface components. They don't have any application logic or business logic responsability. Their only responsibilities are :
- Receiving data to display from Presenter
- Emiting user interaction to Interactor
-
Router Sometimes user interaction leads to changes the whole display scene (master->detail transition for ex). When such user request arrives from View to Interactor, the need to change the current scene is detected by the Interactor but the responsibility of the resulting screen change is on the Router. Decoupling the user action from the navigation effect results in an application more flexible to navigation design changes and greatly simplify deeplinking.
Depedency Inversion Principle
So, as shown in the previous diagram, every application scene is implemented as a VIPER chain of components. During the development of a new Scene, it's comfortable to organize the scene files in a dedicated target, so it builds very quickly (without needing to build the whole app). And since every components interfaces are defined by (protocol) abstractions, faking dependencies is also easy. These 2 benefits allow to have a Test Driven Development (TDD) approach.
When a Scene is developed this way and finished (generally well tested, thanks to TDD), it could be isolated in its own Swift Package, and by this way make the Scene available as dynamic linkable binary.
So the 2-letters folder naming convention is a good preparation of such SPM packaging. When exported into an SPM package, each folders becomes a target inside the package. Please remark that there are no arrow cycles in the diagram, nor in the reference directed graph (doted blue arrows are weak refs), neither in the import direct graph (black arrows). No reference cycle means no memory leak (no retain cycle). No import cycle means is just required for the compiler to beeing able to build the scene.