This is a simple project that gets a TODO list from the network, stores it within Filemanager, creates a memory cache and shows it in a SwiftUI list. The project has 3 screens and it leverages Combine, Observation and async/await to reactively update every screen from a single source of truth. This project leverages all the latest 2024 Apple technologies.
This project can be used as a template for Enterprise grade projects
Note: An older version of this project (without reactivity and with a single coordinator) can be found here: https://github.com/cesmejia/SwiftUI-Clean-Architecture-example-with-unit-tests.git
The project was developed with the following concepts in mind:
No external libraries
SOLID principles
Clean Architecture
MVVM Architecture pattern
Use of Composition root
Coordinator Pattern: Uses UIKit UINavigationController + UITabBarController + UIHostingController for navigation
Factory Pattern
Repository Pattern
Use Cases
Reactivity: Combine CurrentValueSubject + Observation Framework
Communication with Composition Root: Via Delegates (Closures could be used too)
Async Await + Typed Throws
Swift 6 + Complete Strict Concurrency Checking
Dependency Injection
Unit tests: Use of New Swift Testing Framework (Although TDD was not used, tests were created after each instance creation)
Test doubles: Use of Stubs, Spys and Mocks
Folder structure: Domain, Data, Presentation and Framework
This is a very basic project to serve as guide for a tested Clean Architecture approach with SwiftUI.
- Feedback is welcomed.
- I might add some more use cases and features in the near future.
- TODO entity was used throughout the app for simplification sake. True modularity would be achieved by mapping it between layers.
- Why UIKit? I tried using pure SwiftUI but the Composition Root + SwiftUI Tab Navigation escalated the complexity quickly.
- Why CurrentValueSubject? Using it versus PassthroughSubject made simple unit test possible by using the .value parameter. But you could use PassthroughSubject without much issue.
- Why Combine? I tried using pure Async/Await (AsyncStream) or Observation or Combine-@Published but Main Thread issues and Protocol handling escalated the complexity quickly.
- Essential developer course: Essential Developer
- Hacking with swift: HWS
- Clean Mobile Architecture Book by Petros Efthymiou: Clean Mobile Architecture
- Dependency Injection Principles, Practices, and Patterns by Mark Seemann and Steven van Deursen Dependency Injection
- Clean Architecture Book by Robert C. Martin (Uncle Bob) Clean Architecture