|
import SwiftUI |
|
|
|
// MARK: Navigation |
|
|
|
enum Screen: String, Hashable, CaseIterable, CustomStringConvertible { |
|
case first, second, third |
|
|
|
var description: String { |
|
rawValue.capitalized |
|
} |
|
} |
|
|
|
struct MainView: View { |
|
@State private var path = NavigationPath() |
|
|
|
var body: some View { |
|
NavigationStack(path: $path) { |
|
List(Screen.allCases, id: \.self) { screen in |
|
NavigationLink(screen.description, value: screen) |
|
} |
|
.navigationTitle("Home") |
|
.navigationDestination(for: Screen.self) { screen in |
|
Router(screen: screen) |
|
} |
|
} |
|
.environment(\.push, .init { path.append($0) }) |
|
.environment(\.pop, .init { path.removeLast() }) |
|
.environment(\.popToRoot, .init { path = NavigationPath() }) |
|
} |
|
} |
|
|
|
struct Router: View { |
|
let screen: Screen |
|
|
|
var body: some View { |
|
switch screen { |
|
case .first: |
|
FirstScreen() |
|
case .second: |
|
SecondScreen() |
|
case .third: |
|
ThirdScreen() |
|
} |
|
} |
|
} |
|
|
|
// MARK: Screens |
|
|
|
struct FirstScreen: View { |
|
@Environment(\.pop) private var pop |
|
|
|
var body: some View { |
|
VStack { |
|
NavigationLink("Next Screen", value: Screen.second) |
|
Button("Go back") { |
|
pop() |
|
} |
|
} |
|
.buttonStyle(.borderedProminent) |
|
.navigationTitle(Screen.first.description) |
|
} |
|
} |
|
|
|
struct SecondScreen: View { |
|
@Environment(\.pop) private var pop |
|
@Environment(\.push) private var push |
|
|
|
var body: some View { |
|
VStack { |
|
Button("Push after delay") { |
|
Task { |
|
try? await Task.sleep(for: .seconds(0.5)) |
|
push(Screen.third) |
|
} |
|
} |
|
|
|
Button("Go back") { |
|
pop() |
|
} |
|
} |
|
.buttonStyle(.borderedProminent) |
|
.navigationTitle(Screen.second.description) |
|
} |
|
} |
|
|
|
struct ThirdScreen: View { |
|
@Environment(\.popToRoot) private var popToRoot |
|
|
|
var body: some View { |
|
Button("Go back home") { |
|
popToRoot() |
|
} |
|
.navigationTitle(Screen.third.description) |
|
} |
|
} |
|
|
|
// MARK: Environment Actions |
|
|
|
struct PushAction { |
|
let action: (any Hashable) -> Void |
|
|
|
init(_ action: @escaping (any Hashable) -> Void) { |
|
self.action = action |
|
} |
|
|
|
func callAsFunction(_ value: any Hashable) { |
|
action(value) |
|
} |
|
} |
|
|
|
struct PopAction { |
|
let action: () -> Void |
|
|
|
init(_ action: @escaping () -> Void) { |
|
self.action = action |
|
} |
|
|
|
func callAsFunction() { |
|
action() |
|
} |
|
} |
|
|
|
extension EnvironmentValues { |
|
private enum PushActionKey: EnvironmentKey { |
|
static var defaultValue = PushAction { _ in |
|
debugPrint("Push action was invoked, but no value is set") |
|
} |
|
} |
|
|
|
private enum PopActionKey: EnvironmentKey { |
|
static var defaultValue = PopAction { |
|
debugPrint("Pop action was invoked, but no value is set") |
|
} |
|
} |
|
|
|
private enum PopToRootActionKey: EnvironmentKey { |
|
static var defaultValue = PopAction { |
|
debugPrint("Pop to root action was invoked, but no value is set") |
|
} |
|
} |
|
|
|
var push: PushAction { |
|
get { self[PushActionKey.self] } |
|
set { self[PushActionKey.self] = newValue } |
|
} |
|
|
|
var pop: PopAction { |
|
get { self[PopActionKey.self] } |
|
set { self[PopActionKey.self] = newValue } |
|
} |
|
|
|
var popToRoot: PopAction { |
|
get { self[PopToRootActionKey.self] } |
|
set { self[PopToRootActionKey.self] = newValue } |
|
} |
|
} |
This looks amazing! Thank you for sharing! 🚀