TCA: Unidirectional State for SwiftUI
TCA is a strict pipeline for SwiftUI: state and actions enter a pure reducer, emitting new state and effects. Use it when complex flows need reproducible tests without launching the app.
WHY IT EXISTS: SwiftUI and UIKit apps often scatter state across observable objects, delegates, and completion handlers. This makes bugs hard to reproduce because a screen can change for reasons hidden inside the view layer. TCA was built to make every state change explicit, testable, and replayable by forcing all mutations through a single pure function.
THE MENTAL MODEL: Think of a feature as a vending machine. You press a button, which is an action. The machine looks at its current inventory, which is state, and decides what to do next. It updates its internal count and optionally drops a snack into the tray, which is an effect. The display always shows the current inventory, and nothing changes unless a button is pressed. The UI is just the glass front of the machine.
HOW IT WORKS: You define three core pieces for each feature. State is a struct holding all data. Action is an enum representing every event that can happen. The reducer is a pure function that takes state and an action, then returns a new state and an effect. The store owns the runtime. It receives actions, feeds them to the reducer, publishes state changes to SwiftUI views, and executes effects on the side. Effects are values that describe work like network requests, and when they finish they send new actions back into the store. Navigation is also state-driven: if a boolean in state is true, a sheet is presented.
WHEN TO USE IT: Reach for TCA when you are building multi-step flows where later steps depend on earlier state, or when you need to write unit tests that exercise an entire feature without spinning up a live UI. It is especially valuable on teams where multiple engineers touch the same code and need a predictable pattern for where business logic lives.
WHEN NOT TO USE IT: Avoid it for simple static screens or rapid prototypes where a single state property and a callback are enough. TCA requires declaring a state struct, action enum, and reducer even for trivial toggles. If your app has shallow navigation and no shared state, the indirection will slow you down without buying clarity.
ONE CANONICAL EXAMPLE: Consider a search screen. The state contains a query string, an array of results, and an isLoading boolean. Actions include queryChanged, searchResponse, and cancelTapped. When queryChanged arrives, the reducer updates the query, sets isLoading to true, and returns a debounced network effect. If cancelTapped arrives before the response, the reducer can return a cancellation effect. When searchResponse arrives, the reducer appends results and clears isLoading. The view simply reads these fields and sends actions on user input. You can test the entire flow by feeding actions into the reducer and asserting on the state, with no simulator required.
Get five bites like this every day.
Tezvyn delivers a daily feed of 60-second tech bites with quizzes to lock in what you learn.