# Sheets ## Intent Use a centralized sheet routing pattern so any view can present modals without prop-drilling. This keeps sheet state in one place and scales as the app grows. ## Core architecture - Define a `SheetDestination` enum that describes every modal and is `Identifiable`. - Store the current sheet in a router object (`presentedSheet: SheetDestination?`). - Create a view modifier like `withSheetDestinations(...)` that maps the enum to concrete sheet views. - Inject the router into the environment so child views can set `presentedSheet` directly. ## Example: SheetDestination enum ```swift enum SheetDestination: Identifiable, Hashable { case composer case editProfile case settings case report(itemID: String) var id: String { switch self { case .composer, .editProfile: // Use the same id to ensure only one editor-like sheet is active at a time. return "editor" case .settings: return "settings" case .report: return "report" } } } ``` ## Example: withSheetDestinations modifier ```swift extension View { func withSheetDestinations( sheet: Binding ) -> some View { sheet(item: sheet) { destination in Group { switch destination { case .composer: ComposerView() case .editProfile: EditProfileView() case .settings: SettingsView() case .report(let itemID): ReportView(itemID: itemID) } } } } } ``` ## Example: presenting from a child view ```swift struct StatusRow: View { @Environment(RouterPath.self) private var router var body: some View { Button("Report") { router.presentedSheet = .report(itemID: "123") } } } ``` ## Required wiring For the child view to work, a parent view must: - own the router instance, - attach `withSheetDestinations(sheet: $router.presentedSheet)` (or an equivalent `sheet(item:)` handler), and - inject it with `.environment(router)` after the sheet modifier so the modal content inherits it. This makes the child assignment to `router.presentedSheet` drive presentation at the root. ## Example: sheets that need their own navigation Wrap sheet content in a `NavigationStack` so it can push within the modal. ```swift struct NavigationSheet: View { var content: () -> Content var body: some View { NavigationStack { content() .toolbar { CloseToolbarItem() } } } } ``` ## Design choices to keep - Centralize sheet routing so features can present modals without wiring bindings through many layers. - Use `sheet(item:)` to guarantee a single sheet is active and to drive presentation from the enum. - Group related sheets under the same `id` when they are mutually exclusive (e.g., editor flows). - Keep sheet views lightweight and composed from smaller views; avoid large monoliths. ## Pitfalls - Avoid mixing `sheet(isPresented:)` and `sheet(item:)` for the same concern; prefer a single enum. - Do not store heavy state inside `SheetDestination`; pass lightweight identifiers or models. - If multiple sheets can appear from the same screen, give them distinct `id` values.