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

On iPad, the tab bar can become a sidebar and the detail screen keeps its text to a readable column:

Discover, iPad Movie detail, iPad
Discover screen on iPad Movie detail screen on iPad

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 270 lines you can read top to bottom, published as its own package so you can use it in your app too. 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
MVI — what 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.

The table is tested, not just asserted: docs/COMPARISON.md builds the same search feature as MV, MVVM and MVI, and holds all three to one behavioural test suite. It shows where each architecture keeps its guarantees, and which of them only a UI test can reach.

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/
    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
    CinematicComparison/      ← Search as MV, MVVM and MVI, one test suite for all
  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/COMPARISON.md One feature as MV, MVVM and MVI, held to the same tests
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/CinematicDomain && swift test
cd Package/CinematicData && swift test
cd Package/CinematicComparison && 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'

Tests cover every seam: reducer state machines, use-case rules, DTO decoding against captured payloads, repository behavior over a stubbed URLProtocol, persistence round-trips, composition wiring, three black-box UI flows, and an accessibility audit of every screen at the default and largest text sizes. 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.