Migrating to TCA

I watched the composable architecture tour, watched all the episode leading to the tour and watched the tour again. I read all the case studies.

I want to migrate a large application to use TCA and I have no idea where and how to start and every approach I can think of seems very daunting.

Is there a simple mythology I can apply?

I have been tempted to start from scratch but that comes with its own set of problems.

P.S. Why is the Swift Composable Architecture referred to as TCA and not SCA?

The "composable" nature of the architecture should make integrating it into an existing application a matter of slicing off an existing piece of functionality and bundling it up into the various pieces.

  • Identify a single item of state, for example something in the UI that can change based on your app's logic
  • Identify a user/system action (or two) that can affect that state
  • Write a reducer for that state/action
  • Introduce a store for that view
  • Feed actions into the store and bind the store's state to the view

This should be enough to get some simple things to production without radically altering anything in your application, and you should be in a position to write some tests.

Side effects can make things messy, but you don't need to immediately embrace the Effect type and wrangle things. You can instead incrementally take the side effects happening in your view/controller and manually feed their results into the store. Then, over time, you can utilize the Effect type so that the store can do that work for you.

If you have any specific questions please let us know so we can help! In general you should be able to introduce a small store to any part of your application and let it manage a small part of your app's state and logic.

This is kind of a nod to TEA (The Elm Architecture), which influenced part of TCA's design.

Thanks Stephen!

I started refactoring and yes, trying to refactor everything at once was really daunting. I have already refactored my Root View Controller and wrote some nice tests. I am quite happy :slight_smile:

1 Like

Thanks again @stephencelis I've made a lot of progress, implemented that a high order reducer to persist my state to userDefaults, have test coverage for nearly everything, etc.

TCA is simply amazing!!!

I've implemented a package to interface TCA with CoreData and I'll be happy to share it once it matures a bit. I can create a RepositoryEnvironment in my scene delegate :

  let repositoryEnvironment = RepositoryEnvironment(persistentContainer: .live(name: "Spread"))

and use that to instantiate Repositary clients for the states that need to persist to core data. The clients let me perform type safe query (using QueryKit) that return publishers of ValueType (nsmanagedobject are never exposed) or simply return a viewContext or fetchResultController.

One thing that isn't super clear to me is how to have convert my client return effects instead of publishers. Are you gonna do an episode on that? It felt like it was missing at the end of location manager episode.

1 Like

As long as you type your client to return Effect instead of AnyPublisher you should be good to go. For TCA apps we work on we have our dependencies return Effects by default. The only reason we returned AnyPublisher in those episodes was because we were working with a non-TCA app :slight_smile:

Thanks I'll try. I got that you were working on a non-TCA app :) I was just expecting you to show how you went from there to the actual TCA location manager you shipped.

@stephencelis I couldn't find a single example involving Effect<Void, Error> in the case studies and example apps.

I have been using Struct None: Equatable and mapping Void to None and I was wondering if there was a more elegant way to make action(Result<Void, Error>) equatable.

I've created similar structs in the past. You could alternatively transform to an optional MyError?, instead, where nil is success.

I think when tuple equatability is merged we will be able to eliminate that, though!

Or use an action instead of void? Is there anything wrong with that approach?

Terms of Service

Privacy Policy

Cookie Policy