- Application - contains app's entry point files(AppDelegate, SceneDelegate), app configurations(dev, stg, prod) and
- App entry points (AppDelegate, SceneDelegate)
- BusinessLogic
- Models - response, database, domain entities
- Services - Independent services and managers
- Injection - Registering our services in Resolver
- Core
- Base - Base classes for app's modules
- Extensions - Extensions for Foundation, UIKit, Combine, other classes/structs
- Helpers - Independent and reusable tools
- Views - Reusable views(which can be used twice or more times)
- Presentation - all modules in the app (can be grouped by flow)
- Resources - assets, colors, fonts, strings resources
Each module has 4 main components(can be more if you need to split giant logic in some of them):
- View
- Router
- ViewModel
- Builder
Which are connected with ViewModel though the bindings. In this case case ViewModel acts as a connector between View and Router.
- View holds strong reference to ViewModel.
- View listen to ViewModels's publishers.
- View has @State var presentedScreen, every time variable not nil view pushes or presents other View(via ViewModifiers, ex .push, .present)
- ViewModel is independent component.
- Each ViewModel has its own transition publisher where you should notify subscribers(ex. Router) when you should go forward or backward in your flow.
- If ViewModel need some services or parameters, the should be injected with Resolver.
- ViewModel holds strong reference to Router
- ViewModel listens to Routers publishers.
- Router has enum ...Route where all possible transitions are listed.
- Router has didFinish publisher where you shoud notify subsriber(ex. ViewModel) when you should update.
- Each router confirms to protocol Routing
- Returning next View via func view(for route:)
- Builder constructs a Module
- It creates needed components with injecting parameters and wrap it into "Module" type - which is just container with 2 generic types(view and transition, image below)
- When app starts it opens RootView. RootView has variable presentedScreen, which decides what flow is presented now.
- Router creates next module(via Builder) and passes it to View.
- Each flow has entry view, which is NavigationView, and can push or present child views.
- In current implementation RootView changes flow when user is created in UserService. Also you can subscribe to a RootRouter didFinishPublisher in RootViewModel for custom logic.