Flick is a modular Swift demo project built to demonstrate Unidirectional Data Flow (UDF) and Swift Package Manager (SPM) principles in iOS development. This app is designed to be a modern example of scalable architecture, modularization, and clean UI design.
🧠 Architecture: Unidirectional Data Flow (UDF)
Flick implements a UDF approach to ensure:
- Predictable state management
- Clear separation of concerns
- Easier debugging and testing
This project integrates with The Movie Database (TMDB) API to fetch movie data.
-
Create a TMDB account and generate an API key:
https://www.themoviedb.org/settings/api -
Add your API key in the appropriate configuration file or environment.
For this project, issert you API key in kTMDBApiKey property inBaseAPI.swift
public let kTMDBApiKey = "YOUR_API_KEY_HERE"
🛑 Without a TMDB key, the app will immediately produce fatal error.
Modularity is at the heart of Flick. Each feature is encapsulated in its own folder under Modules/
and follows a clean separation of UI, logic, and state.
Most feature modules follow a consistent structure:
📁 ModuleName
├── 📁 State
│ ├── ModuleNameFlow.swift (e.g. SearchFlow.swift)
│ └── ModuleNameForm.swift (e.g. SearchForm.swift)
├── 📁 View
│ ├── ModuleNameComponent.swift (e.g. SearchComponent.swift)
│ └── ModuleNameContainer.swift (e.g. SearchContainer.swift)
├── Middleware.swift (optional side effects, e.g. SearchMiddleware.swift)
This ensures:
- Clear separation of concerns (UI, logic, effects)
- Easy testability
- Scalable modular design
📦 Example: Search
UI consistency is maintained using snapshot testing.
- Tests like
SnapshotTestCase+Component.swift
- UI snapshots are stored under
Snapshots/
Each logical unit (DesignSystem, API, Localization) is a Swift package:
- Improved compile times
- Better code reuse
Flick introduces dynamic reducer composition through the concept of @BindableReducer
.
You can mark any reducer with @BindableReducer
to bind it to a BindableContainer
. This allows:
- 🔁 Dynamic creation/destruction of reducers
- 🔀 Recursive transitions between containers
- 📦 Multiple simultaneous container instances (e.g. opening multiple detail views)
A BindableContainer
manages the lifecycle of dynamic views. UDF will instantiate a separate reducer for each active instance of such a container, maintaining full separation of state and transitions.
Opening several modals or search overlays that can independently manage their own state and close without interfering with each other.
⚠️ Each instance of aBindableContainer
is fully isolated, ensuring clean recursion and reuse.
Licensed under the terms of the LICENSE.