iOS Architecture
This page describes the architecture used in our futuredapp/FuturedKit repository.
The architecture is organized into layers, each with a specific responsibility. This ensures improved code organization, testability, and scalability while minimizing component coupling. We follow modern Swift and SwiftUI best practices.
Overall Architecture
User Interface
The User Interface is built with SwiftUI using Components and ComponentModels. A Component and its corresponding ComponentModel form a single "scene".
- each
Component<Model>is astructconforming toView, parameterized with aModelretained via@State - components bind to a
ComponentModelProtocolto allow mock implementations for seamless scene testing (e.g.,#Previewfeedback) - child components receive their model as a plain property (not wrapped)
- user interactions are forwarded to the
ComponentModelas simple function calls (e.g.,model.onButtonTapped())
Presentation Layer
At the presentation layer, we employ an extended MVVM-C style approach with Flow Coordinators and optional Flow Providers.
- Flow Coordinators conform to
Coordinatorand manage creation, lifetime, navigation stacks, tabs, and modal presentations TabViewFlowfor tabbed flowsNavigationStackFlowfor push/pop flows- coordinators are
@Observableclasses with explicit@MainActorconformance - coordinators control their flow using events emitted by
ComponentModels - Flow Providers encapsulate reusable sub-flows shared across multiple coordinators
Data & Domain Layer
This layer handles shared state, business logic, and dependencies.
DataCache
The DataCache is an @Observable @MainActor class that serves as the Single Source of Truth for shared application data.
- reads and writes are synchronous from any
@MainActorcontext (access viadataCache.value) ComponentModelsuse computed properties to derive data from theDataCache; observation tracking is automaticServicesuseasync/awaitfor asynchronous operations (networking, persistence) and update theDataCachewith results- can be used globally (one per app) or privately within a coordinator to provide flow-specific data
Services
Business logic, such as networking or data processing, is encapsulated in Services.
- responsible for performing external operations (e.g., fetching data from an API)
- used by
ComponentModelsto trigger actions and update theDataCacheif needed
Container
The Container acts as a simple dependency injection hub. It is created at the app's root and passed down to each Coordinator. It is responsible for instantiating and providing shared dependencies such as the DataCache and other Services. Side effects that react to cache changes are co-located in the Container's listeningTask.
Futured Macros
We use futuredapp/futured-macros to reduce boilerplate:
@EnumIdentable— auto-conforms enums toIdentifiable(used on coordinatorDestinationenums, markednonisolated)@ProxyMembers— generates dynamic member lookup forwarding to reduce boilerplate

