simonnickel/snap-navigation
> This package is part of the [SNAP](https://github.com/simonnickel/snap) suite.
Demo project
The demo project shows a navigation hierarchy with 3 top level items to select. It allows infinite items to be pushed or presented as modals and a few deeplinks to navigate to a more complex state.
<img src="/screenshot.png" height="400">
How to use
Define Destinations the App can navigate to:
enum Destination: SnapNavigationDestination {
case triangle, rectangle, circle
var definition: SnapNavigation.ScreenDefinition<Self> {
switch self {
case .triangle: .init(title: "Triangle", systemIcon: "triangle")
...
}
}
}Implement a SnapNavigationProvider to define the structure of reachable destinations:
struct NavigationProvider: SnapNavigationProvider {
var initialSelection: Destination { .triangle }
var selectableDestinations: [Destination] { [.triangle, .rectangle, .circle] }
func parent(of destination: Destination) -> Destination? {
switch destination {
case .triangle, .rectangle, .circle: nil
}
}
}Use SnapNavigationApp in your @main App definition:
@main
struct SnapNavigationDemoApp: App {
var body: some Scene {
SnapNavigationApp(provider: NavigationProvider()) { window, content in
content
.navigationStyle(.single)
// ... setup more global stuff ...
}
}
}```
Use `\.navigator` to navigate in your App:@Environment(\.navigator) private var navigator
navigator(.present(Destination.infinity))
Considerations
TabSection
iOS 18 supports to group multiple Tabs into a TabSection: While the sidebar is visible, the Tabs are visible below the section header. While the TabBar is visible, only the section header is visible as a tab.
This causes ambiguous state when switching size classes or hiding the sidebar. I tried a few things, like manually adding the Section on the NavigationStack. But was not really happy with any of them.
Decision: Not supporting TabSection for now.
.fullScreenCover()
Supporting a mix of .sheet() and .fullScreenCover() causes some animation issues in deeplink handling.
Decision: Not supporting .fullScreenCover() for now. Modal presentation uses .sheet().
// TODO feature: Define FullScreenCover as additional PresentationStyle, which can only be present once as last item with a path to show, no modals. (Or even with its own complete SnapNavigationView and State).
macOS: TabView with .sidebarAdaptable does not maintain state of Tab / Sidebar Item.
Decision: Did not find a way to maintain the navigation state, not worth it at the moment. Reconsider in the future.
AppDestinations + Navigator: Navigator Actions via Environment do not support a generic constraint to a Destination Type.
Decision: Implementation is possible, but causes a lot of boilerplate and redundancy. Left it out for now to encourage using different Destination enums for Features.
// TODO: Fix tapping in background when 2 modals are open closes all modals. (on iPad)
Package Metadata
Repository: simonnickel/snap-navigation
Default branch: main
README: README.md