A blog exploring advanced programming topics in Swift.

Composable navigation beta

Monday Feb 27, 2023

We’ve been teasing navigation tools for the Composable Architecture for a long time, and been working on the tools for even longer, but it is finally time to get a preview of what is coming to the library.

The tools being previewed today include what has been covered in our navigation series so far (episodes #222, #223, #224), as well as a few tools that will be coming in the next few episodes. In particular, this includes the tools for dealing with alerts, confirmation dialogs, sheets, popovers, fullscreen covers, pre-iOS 16 navigation links, and navigationDestination. Notably, this beta does not currently provide the tools for the iOS 16 NavigationStack, but that will be coming soon.

All of these changes are mostly backwards compatible with the most recent version of TCA (version 0.51.0 right now), which means you can point any existing project to the beta branch to get a preview of what the tools have to offer. If you experience any compiler errors please let us know.

The basics

We aren’t going to give a detailed overview of the tools in this announcement and how we motivated and designed them (that’s what the episodes are for 😀), but most of the case studies and demos in the repo have been updated to use the new tools and there is an extensive test suite. There hasn’t been much documentation written yet, but that will be coming soon as the episode series plays out.

Here is a very quick overview of what you can look forward to:

  • When a parent feature needs to navigate to a child feature you will enhance its domain using the new @PresentationState property wrapper and PresentationAction wrapper type:

    struct Parent: ReducerProtocol {
      struct State {
        @PresentationState var child: Child.State?
        …
      }
      enum Action {
        case child(PresentationAction<Child.Action>)
        …
      }
    
      …
    }
    
  • Then you will make use of the new, special ifLet reducer operator that can single out the presentation state and action and run the child feature on that state when it is active:

    var body: some ReducerProtocolOf<Self> {
      Reduce {
        …
      }
      .ifLet(\.$child, action: /Action.child) {
        Child()
      }
    }
    

    That is all that is needed as far as the domain and reducer is concerned. The ifLet operator has been with the library since the beginning, but is now enhanced with super powers, including automatically cancelling child effects when the child is dismissed, and a lot more.

  • There is one last thing you need to do, and that’s in the view. There are special overloads of all the SwiftUI navigation APIs (such as .alert, .sheet, .popover, .navigationDestination etc.) that take a store instead of a binding. If you provide a store focused on presentation state and actions, it will take care of the rest. For example, if the child feature is shown in a sheet, you will do the following:

    struct ParentView: View {
      let store: StoreOf<Parent>
    
      var body: some View {
        List {
          …
        }
        .sheet(
          store: self.store.scope(state: \.$child, action: Parent.Action.child)
        ) { store in
          ChildView(store: store)
        }
      }
    }
    

And that is basically it. There’s still a lot more to the tools and things to learn, but we will leave it at that and we encourage you to explore the branch when you get a chance.

1.0 Preview

As you may have heard recently we have a 1.0 preview available to everyone who wants a peek at what APIs will be renamed and removed for the 1.0 release. Currently that branch is targeting main, but soon it will target this navigation-beta branch, which means you can simultaneously see how to modernize your codebase for the 1.0 and check out the new navigation tools.

Trying the beta

To give the beta a shot, update your SPM dependencies to point to the navigation-beta branch:

.package(
  url: "https://github.com/pointfreeco/swift-composable-architecture",
  branch: "navigation-beta"
),

This branch also includes updated demo applications using these APIs, so check them out if you’re curious!

We really think these tools will make TCA even more fun and easier to use! If you take things for a spin, please let us know (via Twitter, Mastodon or GitHub discussions) if you have any questions, comments, concerns, or suggestions!


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!