Good question! We still don't really have a very strong opinion about TCA… it wasn't really on our radar when learning SwiftUI or building ImmutableData. If you're looking for a detailed "pros and cons" comparing TCA to ImmutableData, I don't really have a great answer for you on that.
One of our goals building ImmutableData was to keep our state management solution light-weight. We don't introduce strong opinions on orthogonal concepts like navigation or dependency injection and we don't import external package dependencies like swift-syntax. Our focus is state management and all the infra is built on top of tools from the Apple SDK: Foundation, Observation or Combine, and SwiftUI. The infra can be imported from the standalone ImmutableData repo or built from scratch following the instructions in Chapter 01 and Chapter 02 of "The ImmutableData Programming Guide".
One of our goals was to keep our infra affordable and reduce the barriers to entry: there is no price. ImmutableData is free. The documentation is free and that includes all the "conceptual" documentation teaching the first principles to build the infra. The sample code and sample application products are free. It's all free and it will remain free.
A more detailed investigation of usability and performance could start with the FoodTruck sample project:
If you are looking for some real-world and head-to-head comparisons based on the state of these frameworks today my suggestion would be for an engineer experienced with TCA to rebuild the incremental migration on Food Truck and preserve the functionality of the ImmutableData migration. If we present that incremental migration here to the community we can then compare them together to see how these different frameworks solve the same problem.
Here would be some requirements for an incremental migration:
-
Start by forking
sample-food-truckfrom the original Apple repo. -
The Food Truck app should continue to deploy back to macOS 13.3 and iOS 16.4.
-
Some bugs we need to fix:
- Editing a
Donutvalue fromDonutEditorleads to stale data displayed:TruckOrdersCarddisplays stale data.OrderCompleteViewdisplays stale data.OrderDetailViewdisplays stale data.OrderRowdisplays stale data.
- Completing an
OrderfromOrdersViewdoes not end a Live Activity that was started fromOrderDetailView.
- Editing a
-
A feature we need to add:
OrdersViewshould add aButtonthat adds a newOrdervalue to our global application state.
-
Some features we need to continue supporting:
- The components we migrate should support Xcode Preview components.
TruckOrdersCardandOrdersViewshould support animation when newOrdervalues are added to our global application state.
-
Some minor bugs that were also fixed during the ImmutableData migration:
- The
TruckOrdersCardcomponent displaysOrdervalues on app launch starting with oldestOrderfirst. TheTruckOrdersCardshould displayOrdervalues starting with newestOrderfirst. - The
OrderDetailViewcomponent should correctly request authorization before attempting to schedule a local notification. - The
OrdersViewcomponent can select anOrdermarked as complete and mark it as complete again. OnlyOrdervalues that are not complete should have the ability to mark as complete. - The
OrdersViewcomponent can select multipleOrdervalues and then attempt to displayOrderDetailView. DisplayingOrderDetailViewshould only be possible if the user has selected just oneOrdervalue.
- The
An important goal during an incremental migration is that migrating individual view components should not break any view components that are still built on the legacy MV* architecture.
Once the incremental migration is complete we can compare the two frameworks to see what they look like stacked against each other:
- Which migration is fewer lines of Swift code added to our project?
- Which migration compiles faster?
- Which migration compiles down to a smaller binary and smaller app install size?
- Which migration uses less CPU at runtime?
- Which migration uses less memory at runtime?
There are also important details that are less easy to measure and quantify:
- Which migration leads to better code quality?
- Which migration leads to less complex view component implementations?
- Which migration leads to better separation of concerns between state and views?
- Which migration is more robust against future changes to our state and views?
- Which migration is easier to maintain over time?
- Which migration is easier to debug?
- Which migration is easier to write unit tests for?
It's also important to consider which migration comes with an easier learning curve. It's not just about how easy it is for the author of the framework to build the incremental migration. How easy would it be for an engineer new to the framework to build the incremental migration or extend it in new directions?