A SwiftUI application demonstrating a scalable state management architecture using composition root pattern and unidirectional data flow.
- Pure Swift Implementation: No third-party dependencies - built entirely with native Swift and SwiftUI
- Modern Swift Features: Leverages latest Swift 6 features including:
- Strict concurrency checking
- Actor-based isolation for thread safety
- Observation framework and macros
- @MainActor annotations
- async/await
- Thread Safety: Implements strict concurrency checks to prevent data races
- Zero Dependencies: Complete control over the codebase with no external dependencies
The application combines principles from Redux and Clean Architecture, creating a hybrid approach that leverages the benefits of both patterns:
- Unidirectional Data Flow: State → View → Action → Reducer → New State
- Single Source of Truth: State is managed in a centralized store
- Immutable State: All state changes are made through pure reducer functions
- Predictable State Updates: Actions are the only way to trigger state changes
- Composition Root: For dependency injection and object graph construction
- Feature-based Structure: Each feature is self-contained with its own views and state management
- Separation of Concerns: Clear boundaries between UI, business logic, and data layers
This hybrid approach provides a scalable foundation while maintaining the simplicity needed for smaller applications.
The application follows a clean, modular architecture with the following key components:
The app uses a composition root pattern for dependency injection and view construction. This is implemented in CompositionRoot.swift
.
Benefits:
- Centralized dependency management
- Clear separation of concerns
- Easier testing through dependency injection
- Single source of truth for object creation
- Better control over object lifecycle
Trade-offs:
- Additional boilerplate code
- Potential complexity in larger applications
- Need to carefully manage shared state
The app implements a custom store pattern for state management.
Benefits:
- Predictable state updates
- Unidirectional data flow
- Clear separation between state, actions, and side effects
- Easy to debug and test
- Thread-safe state updates
Trade-offs:
- More verbose compared to simple @State/@Observable
- Learning curve for developers new to Redux-like patterns
- Overhead for simple state management cases
Uses a dedicated Router class with NavigationPath for handling navigation state.
Benefits:
- Centralized navigation logic
- Type-safe routing
- Easier to maintain and modify navigation flow
- Better separation of navigation concerns from view logic
Trade-offs:
- Additional complexity compared to direct navigation
- Need to maintain route enum
- Potential memory management considerations
ScaleStateApp/
├── Core/
│ ├── CompositionRoot/
│ ├── Router/
│ └── Store/
├── Features/
│ └── Counter/
│ ├── Views/
│ └── Store/
└── Data/
└── Network/
- State Persistence: Store maintains state across navigation
- Async Operations: Handles loading states and API calls
- Clean Navigation: Type-safe routing between views
- Dependency Injection: Clear dependency management through composition root
- Single Responsibility: Each component has a clear, single responsibility
- Dependency Inversion: High-level modules don't depend on low-level modules
- Interface Segregation: Clean separation between different parts of the app
- Immutable State: State changes are handled through actions only
- Thread Safety: Async operations are handled safely
- iOS 17.0+
- Xcode 15.0+
- Swift 6.0
- Strict Concurrency Checking enabled
- SwiftUI 5.0
- Strict Concurrency: All async/await operations are properly marked and checked
- MainActor Usage: UI updates are explicitly handled on the main thread
- Type Safety: Leverages Swift's type system for compile-time safety
- Modern Swift: Uses latest Swift features like macros and property wrappers
- Zero External Dependencies: Built entirely with Apple's native frameworks
- Clone the repository
- Open
ScaleStateApp.xcodeproj
in Xcode - Build and run the project
- Add unit tests for stores and reducers
- Implement deeper navigation stack handling
- Add error handling middleware
- Consider implementing state persistence
- Add logging middleware for debugging