-
Notifications
You must be signed in to change notification settings - Fork 24
NavigationTree
NavigationTree
s define all valid navigation paths in an application
public protocol NavigationTree: PathBuilder
NavigationTree
s compose PathBuilder
s into a NavigationTree
.
As NavigationTree
s are PathBuilder
s themselves, you can compose multiple NavigationTree
s into a bigger NavigationTree
.
struct AppNavigationTree: NavigationTree {
let homeViewModel: HomeViewModel
let detailViewModel: DetailViewModel
let settingsViewModel: SettingsViewModel
var builder: some PathBuilder {
Screen(
HomeScreen.self,
content: { HomeView(viewModel: homeViewModel) },
nesting: {
DetailScreen.Builder(viewModel: detailViewModel),
SettingsScreen.Builder(viewModel: settingsViewModel)
}
)
}
}
struct DetailScreen: Screen {
let presentationStyle: ScreenPresentationStyle = .push
let viewModel: DetailViewModel
struct Builder: NavigationTree {
var builder: some PathBuilder {
Screen(
DetailScreen.self,
content: { DetailView(viewModel: viewModel) }
)
}
}
}
struct SettingsScreen: Screen {
let presentationStyle: ScreenPresentationStyle = .sheet(allowsPush: true)
let viewModel: SettingsViewModel
struct Builder: NavigationTree {
var builder: some PathBuilder {
Screen(
SettingsScreen.self,
content: { SettingsView(viewModel: viewModel) }
)
}
}
}
NavigationTree
use a NavigationTreeBuilder
result builder to compose path builders.
NavigationTreeBuilder
implements the necessary methods to enable Swift's built-in control flow methods, including if, if let and switch.
This means, that you can in/exclude parts of a navigation tree based on a condition.
struct Tree: NavigationTree {
let user: User
var builder: some PathBuilder {
if user.isLoggedIn() {
HomeScreen.Builder(store: homeStore)
} else {
LoginScreen.Builder(store: loginStore)
}
}
}
If your NavigationTree has multiple entrypoints, you can list them in the builder.
A path builder indicates that is responsible of building a path element by returning a valid view hierarchy.
When a path element is built, the path builders are invoked in the listed order. The view hierarchy built by the first matching path builder is returned. If none of the listed path builders are reponsible of building the path element, the path builder returns nil
.
struct Tree: NavigationTree {
let user: User
var builder: some PathBuilder {
EntrypointA.Builder(store: ...)
EntrypointB.Builder(store: ...)
EntrypointC.Builder(store: ...)
// Will never get called, as the first path builder takes care of EntrypointA
EntrypointA.Builder(store: ...)
}
}
public func AnyOf<P: PathBuilder>(
@NavigationTreeBuilder _ builder: () -> P
) -> P
public func If<S: Screen, IfBuilder: PathBuilder, Else: PathBuilder>(
@NavigationTreeBuilder screen pathBuilder: @escaping (S) -> IfBuilder,
@NavigationTreeBuilder else: () -> Else
) -> _PathBuilder<EitherAB<IfBuilder.Content, Else.Content>>
public func If<S: Screen, IfBuilder: PathBuilder>(
@NavigationTreeBuilder screen pathBuilder: @escaping (S) -> IfBuilder
) -> _PathBuilder<EitherAB<IfBuilder.Content, Never>>
public func Empty() -> PathBuilders.EmptyBuilder
public func Screen<
S: Screen,
Content: View,
Successor: PathBuilder
>(
onAppear: @escaping (Bool) -> Void = { _ in },
@ViewBuilder content build: @escaping (S) -> Content,
@NavigationTreeBuilder nesting: () -> Successor
) -> _PathBuilder<NavigationNode<Content, Successor.Content>>
public func Screen<S: Screen, Content: View>(
onAppear: @escaping (Bool) -> Void = { _ in },
@ViewBuilder content build: @escaping (S) -> Content
) -> _PathBuilder<NavigationNode<Content, Never>>
public func Screen<
S: Screen,
Content: View,
Successor: PathBuilder
>(
_ type: S.Type,
onAppear: @escaping (Bool) -> Void = { _ in },
@ViewBuilder content build: @escaping () -> Content,
@NavigationTreeBuilder nesting: () -> Successor
) -> _PathBuilder<NavigationNode<Content, Successor.Content>>
public func Screen<S: Screen, Content: View>(
_ type: S.Type,
onAppear: @escaping (Bool) -> Void = { _ in },
@ViewBuilder content build: @escaping () -> Content
) -> _PathBuilder<NavigationNode<Content, Never>>
Convenience wrapper around PathBuilders.wildcard. Replaces any screen with a predefined one.
func Wildcard<
S: Screen,
ContentBuilder: PathBuilder
>(
screen: S,
pathBuilder: ContentBuilder
) -> _PathBuilder<PathBuilders.WildcardView<ContentBuilder.Content, S>>
Based on the example for the conditional PathBuilder
, you might run into a situation in which your deeplink parser parses a navigation path that can only be handled by the homeScreenBuilder. This would lead to an empty application, which is unfortunate.
To mitigate this problem, you can combine a conditional PathBuilder
with a wildcard PathBuilder
:
.conditional(
either: .wildcard(
screen: HomeScreen(),
pathBuilder: HomeScreen.Builder(store: homeStore)
),
or: wildcard(
screen: LoginScreen(),
loginScreen(store: loginStore)
),
basedOn: { user.isLoggedIn }
)
This is example basically states: Whatever path I get, the first element should be a defined screen.
If you use a wildcard PathBuilder
in as part of an anyOf PathBuilder
, make sure it is the last one in the list. If it isn't, it will swallow all screens and the PathBuilder
s listed after the wildcard will be ignored.
- screen: The screen that replaces the current path element.
- pathBuilder: The
PathBuilder
used to build the altered path.
public func build(pathElement: AnyScreen) -> Builder.Content?
Convenience wrapper around PathBuilders.ifLetStore
public func IfLetStore<
State: Equatable,
Action,
If,
Else,
IfBuilder: PathBuilder,
ElseBuilder: PathBuilder
>(
store: Store<State?, Action>,
@NavigationTreeBuilder then: @escaping (Store<State, Action>) -> IfBuilder,
@NavigationTreeBuilder else: () -> ElseBuilder
) -> _PathBuilder<EitherAB<If, Else>> where IfBuilder.Content == If, ElseBuilder.Content == Else
Convenience wrapper around PathBuilders.ifLetStore
public func IfLetStore<
State: Equatable,
Action,
If,
IfBuilder: PathBuilder
>(
store: Store<State?, Action>,
@NavigationTreeBuilder then: @escaping (Store<State, Action>) -> IfBuilder
) -> _PathBuilder<EitherAB<If, Never>> where IfBuilder.Content == If
associatedtype Builder: PathBuilder
@NavigationTreeBuilder var builder: Builder
An empty navigation tree, building no paths.
func Empty() -> PathBuilders.EmptyBuilder
PathBuilder
responsible of building a single screen. Adds a node to the navigation tree.
func Screen<
S: Screen,
Content: View,
Successor: PathBuilder
>(
onAppear: @escaping (Bool) -> Void,
@ViewBuilder content build: @escaping (S) -> Content,
@NavigationTreeBuilder nesting: () -> Successor
) -> _PathBuilder<NavigationNode<Content, Successor.Content>>
The screen PathBuilder
describes how a single screen is built. The content closure is only called if the path element is of type HomeScreen.
struct Tree: NavigationTree {
var builder: some PathBuilder {
Screen(
onAppear: { initialAppear in ... },
content: { (screen: HomeScreen) in
HomeView(...)
},
nesting: { ... }
)
}
}
The Home screen builder extracts HomeScreen
instances from the navigation path and uses it's nesting PathBuilder
to build the remaining path.
- onAppear: Called whenever the screen appears. The passed bool is true, if it is the screens initial appear.
- content: Closure describing how to build a SwiftUI view given the screen data.
- nesting: Any
PathBuilder
that can follow after this screen
PathBuilder
responsible of building a single screen. Adds a node to the navigation tree.
func Screen<S: Screen, Content: View>(
onAppear: @escaping (Bool) -> Void,
@ViewBuilder content build: @escaping (S) -> Content
) -> _PathBuilder<NavigationNode<Content, Never>>
The screen PathBuilder
describes how a single screen is built. The content closure is only called if the path element is of type HomeScreen
.
struct Tree: NavigationTree {
var builder: some PathBuilder {
Screen(
onAppear: { initialAppear in ... },
content: { (screen: HomeScreen) in
HomeView(...)
}
)
}
}
The Home screen builder extracts HomeScreen
instances from the navigation path and uses it's nesting PathBuilder
to build the remaining path.
- onAppear: Called whenever the screen appears. The passed bool is true, if it is the screens initial appear.
- content: Closure describing how to build a SwiftUI view given the screen data.
PathBuilder
responsible of building a single screen. Adds a node to the navigation tree.
func Screen<
S: Screen,
Content: View,
Successor: PathBuilder
>(
_ type: S.Type,
onAppear: @escaping (Bool) -> Void,
@ViewBuilder content build: @escaping () -> Content,
@NavigationTreeBuilder nesting: () -> Successor
) -> _PathBuilder<NavigationNode<Content, Successor.Content>>
The screen PathBuilder
describes how a single screen is built. The content closure is only called if the path element is of type HomeScreen
.
struct Tree: NavigationTree {
var builder: some PathBuilder {
Screen(
HomeScreen.self,
onAppear: { initialAppear in ... },
content: { HomeView(...) },
nesting: { ... }
)
}
}
The Home screen builder extracts HomeScreen
instances from the navigation path and uses it's nesting PathBuilder
to build the remaining path.
- type: Defines which screens are handled by the
PathBuilder
. - onAppear: Called whenever the screen appears. The passed bool is true, if it is the screens initial appear.
- content: Closure describing how to build a SwiftUI view, if the current path element is of the defined screen type.
- nesting: Any
PathBuilder
that can follow after this screen
PathBuilder
responsible of building a single screen. Adds a node to the navigation tree.
func Screen<S: Screen, Content: View>(
_ type: S.Type,
onAppear: @escaping (Bool) -> Void,
@ViewBuilder content build: @escaping () -> Content
) -> _PathBuilder<NavigationNode<Content, Never>>
The screen PathBuilder
describes how a single screen is built. The content closure is only called if the path element is of type HomeScreen
.
struct Tree: NavigationTree {
var builder: some PathBuilder {
PathBuilders.screen(
HomeScreen.self,
onAppear: { initialAppear in ... },
content: { HomeView(...) }
)
}
}
The Home screen builder extracts HomeScreen
instances from the navigation path and uses it's nesting PathBuilder
to build the remaining path.
- type: Defines which screens are handled by the
PathBuilder
. - onAppear: Called whenever the screen appears. The passed bool is true, if it is the screens initial appear.
- content: Closure describing how to build a SwiftUI view, if the current path element is of the defined screen type.
The if screen PathBuilder
unwraps a screen, if the path element matches the screen type, and provides it to the PathBuilder
defining closure.
func If<S: Screen, IfBuilder: PathBuilder, Else: PathBuilder>(
@NavigationTreeBuilder screen pathBuilder: @escaping (S) -> IfBuilder,
@NavigationTreeBuilder else: () -> Else
) -> _PathBuilder<EitherAB<IfBuilder.Content, Else.Content>>
struct Tree: NavigationTree {
var builder: some PathBuilder {
If { (screen: DetailScreen) in
DetailScreen.Builder(store.detailStore(for: screen.id))
}
else: { ... }
}
}
- screen: Closure defining the
PathBuilder
based on the unwrapped screen object. - else: Fallback pathbuilder used if the screen cannot be unwrapped.
The if screen PathBuilder
unwraps a screen, if the path element matches the screen type, and provides it to the PathBuilder
defining closure.
func If<S: Screen, IfBuilder: PathBuilder>(
@NavigationTreeBuilder screen pathBuilder: @escaping (S) -> IfBuilder
) -> _PathBuilder<EitherAB<IfBuilder.Content, Never>>
struct Tree: NavigationTree {
var builder: some PathBuilder {
If { (screen: DetailScreen) in
DetailScreen.Builder(store.detailStore(for: screen.id))
}
}
}
- screen: Closure defining the
PathBuilder
based on the unwrapped screen object.
Wildcard PathBuilder
s replace any screen with a predefined one.
func Wildcard<
S: Screen,
ContentBuilder: PathBuilder
>(
screen: S,
pathBuilder: ContentBuilder
) -> _PathBuilder<PathBuilders.WildcardView<ContentBuilder.Content, S>>
Based on the example for the conditional PathBuilder
, you might run into a situation in which your deeplink parser parses a navigation path that can only be handled by the homeScreenBuilder. This would lead to an empty application, which is unfortunate.
To mitigate this problem, you can combine a conditional PathBuilder
with a wildcard PathBuilder
:
struct Tree: NavigationTree {
let user: User
var builder: some PathBuilder {
if user.isLoggedIn {
Wildcard(
screen: HomeScreen(),
pathBuilder: HomeScreen.Builder(store: homeStore)
)
} else {
Wildcard(
screen: LoginScreen(),
pathBuilder: loginScreen(store: loginStore)
)
}
}
}
This is example basically states: Whatever path I get, the first element should be a defined screen.
If you use a Wildcard, make sure it is the last one in the list. If it isn't, it will swallow all screens and the PathBuilder
s listed after the wildcard will be unreachable.
- screen: The screen that replaces the current path element.
- pathBuilder: The
PathBuilder
used to build the altered path.
Convenience wrapper around a NavigationTreeBuilder
. Similar to SwiftUI's Group: View
.
func AnyOf<P: PathBuilder>(@NavigationTreeBuilder _ builder: () -> P) -> P
If you ever run into the situation, that your NavigationTree
has more than 10 entrypoints, you can use AnyOf to circumvent the fact that NavigationTreeBuilder
cannot combine more than 10 PathBuilders.
struct Tree: NavigationTree {
var builder: some PathBuilder {
AnyOf {
Empty()
... 9 more path builders
}
AnyOf {
... the other path builders
}
}
}
- builder:
NavigationTreeBuilder
composing multiple path builders into one.
Generated at 2021-04-29T07:59:04+0000 using swift-doc 1.0.0-beta.6.
Types
- AnyPathBuilder
- AnyScreen
- Deeplink
- DeeplinkComponent
- DeeplinkComponent.Argument
- DeeplinkHandler
- DeeplinkParser
- EitherAB
- EitherABC
- EitherABCD
- EitherABCDE
- EitherABCDEF
- EitherABCDEFG
- EitherABCDEFGH
- EitherABCDEFGHI
- EitherABCDEFGHIJ
- IdentifiedScreen
- NavigationNode
- NavigationTreeBuilder
- Navigator
- Navigator.Datasource
- Navigator.DidAppearInvocation
- Navigator.DismissInvocation
- Navigator.GoBackToInvocation
- Navigator.GoToInvocation
- Navigator.GoToPathInvocation
- Navigator.NavigationIdentifier
- Navigator.ReplaceContentInvocation
- Navigator.ReplacePathInvocation
- Navigator.ReplaceScreenInvocation
- NavigatorKey
- OnDismissView
- PathBuilders
- PathBuilders.EmptyBuilder
- PathBuilders.WildcardView
- PathComponentUpdate
- PathUpdate
- Root
- ScreenPresentationStyle
- TreatSheetDismissAsAppearInPresenterKey
- _PathBuilder