A blog exploring advanced programming topics in Swift.

Shared state in the Composable Architecture

Monday Apr 29, 2024

9 weeks ago we began an ambitious new series of episodes to bring first-class state sharing tools to the Composable Architecture, and at the same time we released a public beta of those tools. Over the course of those 9 weeks we had hundreds of conversations with our viewers, fixed dozens of bugs, and implemented a few new features that we didn’t have time to cover in episodes.

And we are now proud to officially release version 1.10 of the Composable Architecture that includes a brand new @Shared property wrapper for sharing state between many features, as well as a few persistence strategies for saving the data to external systems, such as user defaults and the file system.

Join us for a quick overview of the new tools in this release, and be sure to read the migration guide and documentation to best wield the new tools.

@Shared

The core tool added to the Composable Architecture is the @Shared property wrapper. It allows you to introduce state to your features that can be shared with other features, all while embracing value types and not sacrificing testability. One can think of it as being similar to Binding in vanilla SwiftUI, except it is tuned specifically for the Composable Architecture.

To share state in one feature with another feature, simply use the @Shared property wrapper:

struct State {
  @Shared var signUpData: SignUpData
  // ...
}

This will require that SignUpData be passed in from the parent, and any changes made to this state will be instantly observed by all features holding onto it, and if any other feature makes changes to the state our feature will instantly see those changes.

This sounds like we are introducing a reference type to our domain, and technically we are, but while reference types are notoriously tricky to understand in isolation and test since they can be mutated by anyone at anytime and can’t be copied, the Composable Architecture does extra work to make shared state as understandable as possible by making it fully testable, and even exhaustively testable.

Persistence strategies

But we went above and beyond with @Shared. Not only does it allow you to seamlessly share state with multiple parts of your application, but it also allows you to seamlessly persist state to any external system. The library now comes with 2 primary persistence strategies right out of the box, including .appStorage and .fileStorage:

struct State {
  @Shared(.appStorage("hasSeenOnboarding")) 
  var hasSeenOnboarding = false
  
  @Shared(.fileStorage(.currentUserURL))
  var currentUser: User?
}

The above use of the .appStorage persistence strategy allows multiple features to hold onto the same boolean value, and any changes made to it will be automatically synchronized to user defaults on the device. Similarly, the .fileStorage persistence strategy allows all features to see the currently logged-in user, and any changes to the user will be automatically saved to disk.

Further, one can define their own persistence strategies for allowing shared state to be driven from an external system. Really the sky is the limit! With just a little bit of work you can integrate @Shared into a remote config and feature flag system so that you have a simple way of determining when to show certain features:

struct State {
  @Shared(.remoteConfig("showEndOfYearPromotionBanner"))
  var showBanner = false
  
  @Shared(.featureFlag("creatorDashboardV2"))
  var showNewCreatorDashboard = false
}

And of course, if done with care, everything will be 100% testable so that you can make sure your features continue to work correctly even when certain remote config values and feature flags are turned on.

Get started today

We feel that this is one of the most exciting releases we’ve had in the Composable Architecture, and that it solves real problems that users have had from the beginning. Update your apps to version 1.10 of the library to start using the tools today, and be sure to check out the migration guide and documentation to best wield the new tools.


Subscribe to Point-Free

👋 Hey there! If you got this far, then you must have enjoyed this post. You may want to also check out Point-Free, a video series covering advanced programming topics in Swift. Consider subscribing today!