Skip to content
tezvyn:

Design state-driven navigation for a conditional wizard in UIKit and SwiftUI

Source: developer.apple.comHardHow cards are made

Design state-driven navigation for a conditional wizard in UIKit and SwiftUI

This tests separating navigation state from UI. Model the flow as a state machine enum, derive the stack from state, and unit test transitions in plain Swift. A red flag is imperative pushes inside views or view controllers.

What's really being asked

This question probes your ability to separate navigation concerns from presentation code in a branching user flow. The interviewer wants to see if you treat the wizard sequence as data rather than a series of imperative side effects, and whether you understand how to make that sequence unit testable without launching a UI. They also care that you know the idiomatic iOS patterns for both UIKit and SwiftUI rather than forcing one into the other.

The full answer

First, model the wizard as a finite state machine using an enum with associated values, where each case represents a screen and carries the data needed for that step. Second, create a single source of truth object such as a WizardRouter or WizardViewModel that owns the current state and computes valid next steps based on user input. Third, for UIKit, use a coordinator pattern where the coordinator owns the UINavigationController and translates state changes into push or pop operations, keeping view controllers dumb and event-only. For SwiftUI, bind a NavigationStack to a collection of those enum cases or a NavigationPath, and mutate that collection from the model so the view is purely declarative. Fourth, ensure all transition logic lives in plain Swift types with no UIKit or SwiftUI imports so you can write fast unit tests that assert the exact stack given a set of user answers.

The mistakes people make

A major red flag is imperative navigation scattered through view controllers or SwiftUI views, such as calling pushViewController from a button action inside a view controller or using NavigationLink with isActive booleans managed in view state. Another red flag is embedding conditional branching directly into view body code or view controller lifecycle methods, which makes the flow impossible to unit test and prone to drift between platforms. Suggesting you need to wrap a UIKit navigation controller inside SwiftUI to handle complex flows is also a sign of weak SwiftUI knowledge.

What usually comes next

The interviewer may ask how you prevent a user from advancing until validation passes, which you solve by gating the state transition on a validation result. They may ask how you handle going back to edit a previous answer while preserving downstream choices, which you solve by recomputing the tail of the stack from the modified state. They might also ask about supporting deep links or state restoration, serializing the enum array to disk, or how to remotely configure the flow graph without an app update.

A concrete example

Imagine a loan application wizard. The user enters personal info, selects loan type, and then either sees income verification for personal loans or business details for commercial loans. In UIKit, a LoanCoordinator holds a UINavigationController and a currentState property; when the user picks commercial, the coordinator transitions state and pushes the business details view controller. In SwiftUI, a LoanViewModel exposes a NavigationPath of LoanRoute values; the root view wraps a NavigationStack around the path binding, and choosing commercial appends LoanRoute.businessDetails to the path. A unit test creates the view model, sends the selectLoanType event, and asserts the path contains exactly personalInfo followed by businessDetails, all without constructing any views.

Interview question

Which approach correctly separates navigation from presentation in a unit-testable conditional wizard for both UIKit and SwiftUI?

  • a.Embed branching logic directly in the view body and view controller lifecycle methods, wrapping a UINavigationController for use in SwiftUI.
  • b.Use a UIKit coordinator but manage the SwiftUI stack with local @State variables mutated inside onAppear handlers.
  • c.Model the flow as a state machine enum with plain Swift transition logic and derive the UIKit and SwiftUI stacks from that state.Correct
  • d.Store the current step in a view model and use NavigationLink isActive booleans in SwiftUI while calling pushViewController from UIKit button actions.
Why?

Modeling the wizard as a state machine enum with plain Swift transition logic lets you derive the stack for both UIKit and SwiftUI and unit test transitions without launching a UI. Option B is tempting because coordinators correctly separate UIKit concerns, but relying on local @State inside onAppear handlers still buries imperative navigation inside the view rather than driving it declaratively from the model.

Just read this? Test yourself on what you have been reading.

Read the original → developer.apple.com

You just looked this up. Could you explain it out loud?

That is the part interviews actually test. Tezvyn takes questions like this one and gives you what the interviewer is really checking, the answer that lands, and the mistake that ends the conversation, in the four minutes before your next meeting.

The iPhone app is on the way

We are building it. Until it lands, nothing here is held back from you: every interview card, your saved cards, streaks and the job board all work in Safari, plus hundreds of free practice quizzes of thirty questions each. Sign in and it all carries over to the app the day it arrives.

Want it as an icon? Tap Share at the bottom of Safari, then Add to Home Screen. It opens full screen and the cards you have read stay available offline.

Get it on Google PlayiPhone app coming soon

We are hiring for this. Open roles that interview on ios — each one lists the topics its interview covers.

See open roles