Skip to content

SwiftUI navigation, presentation, alerts, and deep links

Navigation is application state with a visual presentation. Model destinations as data and the interface can validate deep links, restore context, and adapt between stack and split layouts.

Define routes with domain identity

enum Route: Hashable {
case note(FieldNote.ID)
case settings
}
struct RootView: View {
@State private var path: [Route] = []
var body: some View {
NavigationStack(path: $path) {
NoteListView(open: { id in path.append(.note(id)) })
.navigationDestination(for: Route.self) { route in
switch route {
case .note(let id): NoteDetailScreen(noteID: id)
case .settings: SettingsView()
}
}
}
}
}

The path records intent, not whole mutable model objects. A destination resolves the ID against current data and can show a missing-note state.

Model focused presentation

enum Sheet: Identifiable {
case newNote
case edit(FieldNote.ID)
var id: String {
switch self {
case .newNote: "new"
case .edit(let id): "edit-\(id)"
}
}
}
@State private var sheet: Sheet?

One optional value prevents several sheet Booleans from becoming true together. The item passed to .sheet(item:) also identifies the presented task.

Alerts benefit from the same approach:

struct DeleteRequest: Identifiable {
let id: FieldNote.ID
let title: String
}
@State private var deleteRequest: DeleteRequest?

The confirmation carries the exact note and copy needed for the decision. Confirm invokes one deletion path. Cancel clears the request without mutation.

enum DeepLink {
case note(FieldNote.ID)
init?(url: URL) {
guard url.scheme == "fieldnotes",
url.host == "note",
let rawID = url.pathComponents.dropFirst().first,
let id = FieldNote.ID(rawValue: rawID) else { return nil }
self = .note(id)
}
}

Parsing validates syntax. Routing validates application state: does the note exist, may this user open it, and which window owns the request? Invalid or missing destinations need visible recovery.

Universal links also require associated domains, a hosted association file, signing, installation, and device testing. URL parsing alone does not prove them.

Restore durable route state carefully

Routes can be encoded for restoration when their values are stable. On restore, reconcile them against current notes and permissions. Drop or replace invalid destinations rather than crashing or manufacturing stale objects.

Compact and regular layouts may present the same selection differently. Keep the route or selection model shared while adapters choose stack, column, sheet, or window presentation.

Avoid navigation ownership leaks

Leaf rows can emit intent such as open(note.id). They should not locate a global navigator and mutate arbitrary paths. Keeping route mutation at a feature boundary makes previews and tests smaller.

Validation boundary

The route examples were not compiled. Navigation transitions, sheet and alert presentation, state restoration, universal-link association, Simulator routing, signing, and physical-device delivery remain Not verified.

Series navigation

References