This is a fork of the SwiftDataTCA repo by Rodrigo Santos de Souza that I've used to explore how best to incorporate SwiftData into an application written using The Composable Architecture v1.11.2 (TCA) library and tools. My changes were done in Xcode 16.0 Beta using Swift 6 with Complete Concurrency Checking enabled.
The code contains two top-level TCA "features" (combination of a reducer and a SwiftUI view):
- FromStateFeature -- the list of movies to show and work with comes from a SwiftData query done in the feature
- FromQueryFeature -- the list of movies to show and work with
comes from a
@Query
macro in the SwiftUI view
Both features contain the same interface functionality, including:
- Adding a new "random" movie
- Sorting movies by title
- Searching by title content
- Swiping to mark as a favorite
- Swiping to delete a movie
- Selecting a movie to "drill-down" to a list of actors. This view too supports "drilling-down" to see the actor's movies.
Per TCA guidance, all UI activity lead to reducer actions that are performed in the feature's reducer logic, updating internal feature state when necessary to cause a UI update.
The two features are quite similar and there is some duplication of code, but this was done to make the features self-contained.
The top-level views FromStateView and
FromQueryView start with a TCA NavigationStack
view
builder. The subsequent
List
views define NavigationLink
elements for each movie or actor in the view. These drive the transitions into
the next view, and record the path for the Back
button to follow when moving back up.
The top-level reducers monitor the path
actions and handle the selections made by the child views,
ActorMoviesView
and
MovieActorsView.
This is pretty much as what is documented on TCA's Pushing Features onto the
Stack
page.
Each of the drill-down views can also change the favorite state of a movie, either via toolbar button in
the MovieActorsView
view, or via swiping in the ActorMoviesView
view. When a parent view comes back into view, it
updates to account for any possible changes made by a popped view.
The SwiftUI previews operate pretty much like in the simulator or on a physical device.
For the most part, SwiftData functionality is not found in the features. Instead, they use value types in the latest
schema (v6) derived from the model reference types via their valueType
attrribute.
The value types have the persistentModelID
attribute of
the model that they are based on. The value types do not contain relationships like their
model counterparts. Instead they provide functions that return the relations as a collection of value types that are
ordered in a desired way.
The Database
struct prvides an abstraction the the features use to interact with the SwiftData backend. It currently
offers 4 functions:
- fetchMovies -- return the collection of movies for a given SwiftData
FetchDescriptor
- add -- create a new movie and add it to the database
- delete -- delete an existing movie
- save -- save to the database any changes that have been made to existing models
Per TCA guidance, there are live, test, and preview instances of this Database
instance depending on the runtime
environment. Since the differences are found in the underlying model context (which is also a dependency), the live
and test instances are basically the same, but with different ModelContainer
and ModelContext
instances.
Currently, there are 6 schemas defined by the application -- two were from the fork. There are migrations from one to the next. The migrations to v3 and v4 are complex migrations:
- v3 contains a new
sortableTitle
attribute on the Movie entity - v4 contains a new
Actor
entity and establishes many-to-many relationship between it andMovie
entity - v5 contains the same models, but removes the
id
attribute that was populated with a UUID value - v6 contains the same models but renames them to have a "Model" suffix. This was done to allow the names without "Model" to refer to the value type based on the model.
There are defined migrations from one schema to the next. The migrations have tests that validate the migration behavior. This is one area that is not well documented in the current Swift Data documentation, and the migrations may not be optimal, but they do appear to do the right thing.
There are four sets of tests that exercise the code:
- old-school XCTest collection that exercises the TCA routing and other non-UI code
- new-school Swift Testing style collections that for now replicate old-school schema tests
- snapshot tests that run against the preview renders. These tests run on GIthub but the comparison check is disabled -- the images generated with Xcode 16.0 likely will not match those generated by the older Xcode used in the integration pipeline.
- UI tests that attempt to exercise some flows while running under the app in a simulator.
Note
Due to how the in-memory SwiftData containers are implemented, tests that rely them cannot run in parallel mode. Doing so will result in random test failures and crashes.
Below is the original contents of the "README" document found at the time of the fork.
This is a sample application that demonstrates the power of the PointFree Composable Architecture (TCA) in combination with Apple's SwiftData for data persistence.
This project is a demonstration of how to implement SwiftData and the PointFree Composable Architecture (TCA) in an iOS application. It provides a clear and concise example of how to integrate these two powerful tools to build efficient and maintainable iOS apps.
-
Sample Implementation: The project is a simple implementation that highlights the integration of SwiftData for data persistence and TCA for a functional and predictable architecture.
-
SwiftData Integration: SwiftData is used to illustrate the ease of data persistence in iOS applications, making it a valuable addition to your toolbox.
-
TCA Showcase: The project demonstrates how TCA can be used to structure your app's architecture, making it easier to understand and manage the application's behavior and state.
To get started with this project, follow these steps:
- Clone the Repository:
- Open Xcode:
- Build and Run: Build and run the project in Xcode to see the Movie app in action.
You are encouraged to use this project as a reference to understand how to implement SwiftData and TCA in your own iOS applications. The sample code provided here can help you kickstart your projects and build efficient, maintainable apps.
If you'd like to contribute to this project, please follow the CONTRIBUTING.md guidelines.
This project is licensed under the MIT License - see the LICENSE file for details.