The Composable Architecture (TCA), which recently reached version 1.13, is an "ergonomic" Swift library that provides a general framework to address commonplace problems when you build an app, including state management, feature composition, side effect management, and testing.
The Composable Architecture builds on a few key concepts, including state, actions, reducers, and store. Reducers and stores are concepts that will be familiar to React programmers using Redux but are quite novel for native iOS development. According to the library creators, Brandon Williams and Stephen Celis, this approach enables breaking large, complex features into smaller domains that can be glued together.
While the Composable Architecture can used with both UIKit and Swift, its design has been heavily inspired by SwiftUI and complements it well, the library creators say. This makes the library API especially ergonomic for developers familiar with SwiftUI and its patterns.
In particular, TCA provides an @ObservableState
macro that works like iOS 16 @Observable
and allows to observe minimal state changes as SwiftUI does. TCA also borrows the SwiftUI approach to composability, where each feature is a type providing a body
property and mimics the behavior of SwiftUI’s @Environment
property wrapper to handle the specification of dependencies using the @Dependency
property wrapper.
The following code snippet shows how you would model a simple up-down counter feature:
@Reducer
struct Feature {
@ObservableState
struct State: Equatable {
var count = 0
var numberFact: String?
}
enum Action {
case decrementButtonTapped
case incrementButtonTapped
case numberFactButtonTapped
case numberFactResponse(String)
}
var body: some Reducer<State, Action> {
Reduce { state, action in
switch action {
// implement here all actions;
// they modify state or have side effects
}
}
}
}
As you can see in the snippet, the state of the app and actions available to the user are modelled using specific types, while the body
method is responsible for updating the state for each available action.
One key aspect in TCA is state sharing, which is accomplished using the @Shared
macro, which you can see works similarly to SwiftUI @Binding
by exposing a publisher
property enabling to observe changes to a reference from any part of an application. The @Shared
property wrapper also supports persisting data to memory, user defaults, a generic file, or using a custom mechanism.
TCA relies largely on common UI elements available in SwiftUI, such as sheet(item:)
, popover(item:)
, and NavigationStack
, but additionally provides its own navigation mechanism that more closely adapts to state-driven apps. The library supports two main paradigms of navigation, tree-based and stack-based navigation.
Adopting a library as the foundation for the architecture of an app can be quite a challenging decision to make, the library authors admit, but there are cases where it could be the best approach instead of trying to reimplement everything from scratch:
If a library's core tenets align with your priorities for building your app, then adopting a library can be a sensible choice. It would be better to coalesce on a well-defined set of tools with a consistent history of maintenance and a strong community than to glue together many "tips and tricks" found in blog posts scattered around the Internet.
The Swift Composable Architecture can be cloned from GitHub or installed from the Swift Package Index. It has over 12,000 stars and 1,400 forks on GitHub with over 200 contributors, which makes it the most popular Swift architectural library among those listed on the Swift Package Index.