The NavigationKit is a library thats extends SwiftUI implementation for NavigationStack (iOS 16+ only) and adds more resources to managed the user interface.
This repository is distributed through SPM, being possible to use it in two ways:
- Xcode
In Xcode 14, go to File > Packages > Add Package Dependency...
, then paste in
https://github.com/brennobemoura/navigation-kit.git
- Package.swift
// swift-tools-version: 5.7
import PackageDescription
let package = Package(
name: "MyPackage",
products: [
.library(
name: "MyPackage",
targets: ["MyPackage"]
)
],
dependencies: [
.package(url: "https://github.com/brennobemoura/navigation-kit.git", from: "1.0.0")
],
targets: [
.target(
name: "MyPackage",
dependencies: ["NavigationKit"]
)
]
)
The main features available are the ones listed down below. For each one there is a problem solving solution that was developed thinking to solve the coupled SwiftUI's View
struct ContentView: View {
var body: some View {
NKNavigationStack {
FirstView()
}
}
}
Using the NKNavigationStack replaces the NavigationPath with NavigationAction that allows developers to manipulate in a better way the current stacked views.
struct FirstView: View {
@Environment(\.navigationAction) var navigationAction
var body: some View {
Button("Push") {
// SomeModel needs to be mapped using
// navigationDestination(for:destination:)
// using SwiftUI's method.
navigationAction.append(SomeModel())
}
}
}
struct FirstView: View {
@Environment(\.viewResolver) var viewResolver
var body: some View {
// SomeModel needs to be mapped using
// viewResolver(for:_:) {}.
viewResolver(SomeModel())
}
}
To map the model with the corresponding view, it's available the viewResolver(for:_:) that needs to be specified one view before the usage.
struct ContentView: View {
var body: some View {
NKNavigationStack {
FirstView()
.viewResolver(for: SomeModel.self) {
SecondView($0)
}
}
}
}
struct FirstView: View {
@Environment(\.sceneAction) var sceneAction
var body: some View {
Button("Push") {
// SomeModel needs to be mapped using
// sceneAction(for:perform:) and
// the sceneActionEnabled() called in the root
// hierarchy.
sceneAction(SomeModel())
}
}
}
To map the action it's necessary to call the sceneAction(for:perform:) method which will capture the action thrown in every place that it might be listened.
struct ContentView: View {
var body: some View {
NKNavigationStack {
FirstView()
}
.sceneAction(for: SomeModel.self) {
print("Action received: \($0)")
}
// but, sceneActionEnabled is needed before
// somewhere in the application
.sceneActionEnabled()
}
}
Suggestion: call sceneActionEnabled in App's body property.
The ViewModelConnection makes possible to connect a ViewModel into a View keeping the SwiftUI State sync and upright.
This implementation was design to be used inside Coordinator struct.
struct SecondCoordinator: View {
let model: SomeModel
var body: some View {
ViewModelConnection(model, SecondViewModel.init) { viewModel in
SecondView(viewModel: viewModel)
}
}
}
To managed the flow as Coordinator was meant to be, you need to specify the destination property for the ViewModel as you can work like this:
struct SecondCoordinator: View {
let model: SomeModel
var body: some View {
ViewModelConnection(model, SecondViewModel.init) { viewModel in
SecondView(viewModel: viewModel)
.onReceive(viewModel.$destination) { destination in
switch destination {
case .error(let error):
errorScene(error)
case .third(let third):
thirdScene(third)
case .none:
break
}
}
}
}
}
Inside the errorScene or thirdScene you can call the navigationAction or sceneAction to perform something.