Cinematic

MVI + Clean Architecture for SwiftUI, shown working. Cinematic is a complete movie app for iOS 26 — Swift 6, five layered Swift packages, typed throws end to end, and tests at every seam. It exists to be read: every decision is documented, and nothing is a toy stub.

CI Swift 6 iOS 26+ License: MIT Coverage Docs GitHub stars

Discover Movie detail Discover, dark Detail, dark
Discover screen, light mode Movie detail screen, light mode Discover screen, dark mode Movie detail screen, dark mode

No API keys, no accounts, no setup. Clone, open, run — the catalog comes from Apple’s public iTunes feeds.

Read the full walkthrough on the documentation site →

Contents

Why this repository exists

Most architecture write-ups demonstrate MVI on a counter and Clean Architecture on a diagram. The gap is the middle: a codebase small enough to read in an evening but real enough to face what production code faces — an API with quirks, caching, cancellation, localization, accessibility, error states, and tests that prove the design claims. Cinematic is that middle.

The architecture in one diagram

graph TD
    App["Cinematic (app target)<br/>composition root + navigation"]
    Presentation["CinematicPresentation<br/>reducers, views, view state"]
    Domain["CinematicDomain<br/>entities, use cases, contracts"]
    Data["CinematicData<br/>API client, DTOs, repositories"]
    Design["CinematicDesign<br/>tokens + components"]
    MVIKit["MVIKit<br/>Store, Reducer, Effect"]

    App --> Presentation
    App --> Data
    Presentation --> MVIKit
    Presentation --> Domain
    Presentation --> Design
    Data --> Domain

The arrows are the whole doctrine. Dependencies point inward, and the package manifests enforce it: CinematicPresentation cannot import CinematicData because its Package.swift never declares it. Only the app target — the composition root — knows concrete data sources and injects them behind domain protocols.

The MVI loop

flowchart LR
    View -- "send(intent)" --> Store
    Store -- "reduce(&amp;state, intent)" --> Reducer
    Reducer -- "mutated state + Effect" --> Store
    Store -- "runs Effect" --> Task["async effect"]
    Task -- "send(intent)" --> Store
    Store -- "observed state" --> View

The whole machinery is MVIKit — about 250 lines you can read top to bottom. docs/MVI.md walks through every type.

What it demonstrates

How it compares: MVI vs MVVM vs MV vs TCA

SwiftUI leaves room for several architectures, and the right one depends on how hard the state is. Model-View-Intent earns its keep when state is genuinely hard — concurrent loads, cancellation, multiple sources you must keep consistent. When it isn’t, a lighter pattern wins:

Pattern Reach for it when The trade-off
MV (@Observable model) A screen has one source of truth and little logic View and logic blur as the screen grows
MVVM You want testable view logic without much ceremony No built-in story for side effects or cancellation
MVIwhat Cinematic uses State has races, cancellation, or several sources to reconcile One reducer plus effects per feature is more structure upfront
TCA A large app needs composable features and exhaustive test tooling A third-party dependency and a steeper learning curve

MVIKit is the smallest thing that makes the MVI guarantees true — read it before reaching for a framework, so you know exactly what a framework would buy you. docs/MVI.md goes deep on the reducer, effect cancellation, and testing.

Quick start

git clone https://github.com/Khizanag/Cinematic-iOS.git
cd Cinematic-iOS
open Cinematic.xcodeproj

Select the Cinematic scheme and run on any iOS 26 simulator. Requires Xcode 26 or later.

Project layout

Cinematic-iOS/
  Cinematic.xcodeproj
  Cinematic/                  ← app target: composition root, navigation, About
    App/
      Navigation/             ← coordinators, Screen/Sheet/Cover, factories
      Dependencies.swift      ← the composition root
    Feature/About/
    Resource/
  CinematicTests/             ← composition + deep-link tests
  CinematicUITests/           ← XCUITest flows over the stubbed world
  Package/
    MVIKit/                   ← the pattern: Store, Reducer, Effect, LoadingPhase
    CinematicDomain/          ← entities, use cases, repository contracts
    CinematicData/            ← iTunes API, DTOs, mappers, caching, persistence
    CinematicDesign/          ← DesignSystem tokens + reusable components
    CinematicPresentation/    ← the four features, one folder each
  docs/                       ← the long-form documentation

Documentation

Read it all on the documentation site, or in-repo:

Document Covers
docs/ARCHITECTURE.md The layers, the dependency rule, the composition root, concurrency, trade-offs
docs/MVI.md Every MVIKit type, effect cancellation, bindings, testing stores
docs/ADDING-A-FEATURE.md The step-by-step recipe, from use case to pushed screen
docs/TESTING.md What each layer’s tests prove and how the doubles work
docs/ACCESSIBILITY.md The VoiceOver, Dynamic Type, Reduce Motion / Transparency, and localization story

Each package also carries its own short README.

Testing

# Platform-agnostic packages — run anywhere, no simulator
cd Package/MVIKit && swift test
cd Package/CinematicDomain && swift test
cd Package/CinematicData && swift test

# UI packages and the app — run on an iOS 26 simulator
xcodebuild test -scheme CinematicDesign -destination 'platform=iOS Simulator,name=iPhone 17'
xcodebuild test -scheme CinematicPresentation -destination 'platform=iOS Simulator,name=iPhone 17'
xcodebuild test -project Cinematic.xcodeproj -scheme Cinematic -destination 'platform=iOS Simulator,name=iPhone 17'

79 tests across six suites: reducer state machines, use-case rules, DTO decoding against captured payloads, repository behavior over a stubbed URLProtocol, persistence round-trips, composition wiring, and three black-box UI flows. swiftlint --strict passes with zero violations.

Contributing

Issues and pull requests are welcome — see CONTRIBUTING.md. If this project made MVI or Clean Architecture click for you, a ⭐️ helps other iOS developers find it.

License

MIT — see LICENSE.