Ask
74

navigationstack path enum or a coordinator for 31 screens with deep links navigation

Rewriting navigation in an app with 31 distinct screens. Two of us, both iOS, no plans to add a third.

Requirements that are not negotiable: universal links have to open any screen with the right back stack behind it, and state restoration has to bring people back where they were after the app is killed.

Option A is the built-in one - a typed path array of an enum, navigationDestination(for:) at the root, and a URL parser that maps a link to an array of routes.

Option B is a coordinator layer - protocol per flow, each owning its own child routes, views never know what comes next.

I have used coordinators on UIKit projects and liked them there. I cannot tell if that instinct still applies now that the navigation state is a value I can just hold.

Which one did you regret less?

8 answers Share
Report

Answering anonymously — a moderator will review it first.

  • @probe_to_ground · 3w ago · 2 replies

    Coordinators still earn their place in two situations, and 31 screens is not automatically one of them:

    • Modal presentation that stacks. Sheets over sheets, full screen covers, alerts that must appear over a specific layer. The stack does not model that, so if your app is presentation-heavy you end up with coordination logic somewhere regardless, and putting it in one named place beats scattering @State private var showingX across twelve views.
    • Flows that get reordered by experiments or feature flags. If onboarding has four steps whose order changes based on a remote config, having one object that decides "what comes after step 2" is genuinely better than each screen knowing its own successor.

    Neither of those is "we have a lot of screens". Screen count argues for a well-organised route enum, not for another layer.

    43
    Share
    Reply

    Answering anonymously — a moderator will review it first.

    Report
    • @slowmiles_kat · 2w ago

      Agreed on modals. The pragmatic middle is one path per presentation context - the root stack, plus a separate small path for each sheet that can itself push. Explicit, no framework fighting, no coordinator protocol per flow.

      19
      Share
      Reply

      Answering anonymously — a moderator will review it first.

      Report
  • @rest_day_rita · 2w ago · 3 replies

    Typed path. The coordinator pattern existed because UIKit navigation state was hidden inside a navigation controller and you had to build a parallel structure to reason about it. That problem is gone - the stack is now an array you own, and a coordinator on top of it is a second abstraction over something already abstract.

    Concretely, with path: [Route] where Route is an enum:

    • deep linking is a pure function URL -> [Route], and setting path to the result is the navigation. Back stack included, for free, because you built the whole array.
    • testing navigation is asserting on an array. No view layer, no simulator.
    • state restoration is making Route conform to Codable and persisting the array.

    That third point is why I would push you hard on this: Route: Codable from day one, even before you need restoration. Retrofitting it later means every associated value has to become encodable, and you will find one that holds a reference to a service object and have to redesign it.

    One warning: keep associated values to identifiers, not whole model objects. case listing(id: String) not case listing(Listing). Otherwise you serialise a snapshot of a model into your navigation state and show stale data after restoration.

    64
    Share
    Reply

    Answering anonymously — a moderator will review it first.

    Report
    • @two_calendars · 3w ago

      Same conclusion on the Compose side, for what it is worth - a sealed interface of destinations plus a serializable route type. The frameworks converged on the same shape, which is usually a sign it is the right one.

      18
      Share
      Reply

      Answering anonymously — a moderator will review it first.

      Report
    • @petra_lindqvist · 3w ago

      "Codable from day one" is exactly the kind of thing I would have skipped and paid for. Ids only makes obvious sense once you say it out loud.

      22
      Share
      Reply

      Answering anonymously — a moderator will review it first.

      Report
  • @secondshooter_v · 2w ago

    Whichever you pick, decide early who is allowed to mutate the path. The failure mode I have watched happen twice is that half the screens push directly and half go through a router, and then a deep link lands in a stack assembled two different ways and the back button does something incoherent.

    One owner for the array. Views send intent, the owner decides.

    26
    Share
    Reply

    Answering anonymously — a moderator will review it first.

    Report
  • @finops_reyna · 3w ago

    Practical note on universal links: test the cold-start case specifically. Link arriving while the app is already running is easy. Link arriving as the process launches, before your root view has appeared, is where you find the ordering bug - you set the path and something resets it during initial layout. Buffer the incoming route until the root is ready.

    12
    Share
    Reply

    Answering anonymously — a moderator will review it first.

    Report
  • @carryon_only · 2w ago

    Two people, 31 screens, no third dev coming. Pick the thing with fewer files.

    5
    Share
    Reply

    Answering anonymously — a moderator will review it first.

    Report