diff --git a/ExampleApp/Shared/DiveScreens.swift b/ExampleApp/Shared/DiveScreens.swift index f41a6dd..be1226e 100644 --- a/ExampleApp/Shared/DiveScreens.swift +++ b/ExampleApp/Shared/DiveScreens.swift @@ -10,9 +10,9 @@ import Navigator struct GreenScreen: ScreenView { - @EnvironmentObject var navigator: Navigator + @EnvironmentObject var navigator: Navigator @State var showNextScreen: Bool = false - var currentScreen: Screens + var screenId: ScreenID var body: some View { Color.green @@ -51,9 +51,9 @@ struct GreenScreen: ScreenView { struct BlueScreen: ScreenView { - @EnvironmentObject var navigator: Navigator + @EnvironmentObject var navigator: Navigator @State var showNextScreen: Bool = false - var currentScreen: Screens + var screenId: ScreenID var body: some View { @@ -95,14 +95,13 @@ struct BlueScreen: ScreenView { struct BlueScreen_Preview: PreviewProvider { static var previews: some View { - let blueScreen = Screens.blueScreen() + let blueScreen = ScreenID.blueScreen() - BlueScreen( - currentScreen: blueScreen) + BlueScreen(screenId: blueScreen) .environmentObject( Navigator( rootScreen: blueScreen, - viewFactory: MyViewFactory() + viewFactory: AppViewFactory() ) ) } @@ -113,13 +112,13 @@ struct BlueScreen_Preview: PreviewProvider { struct GreenScreen_Preview: PreviewProvider { static var previews: some View { - let greenScreen = Screens.greenScreen() + let greenScreen = ScreenID.greenScreen() - GreenScreen(currentScreen: greenScreen) + GreenScreen(screenId: greenScreen) .environmentObject( Navigator( rootScreen: greenScreen, - viewFactory: MyViewFactory() + viewFactory: AppViewFactory() ) ) } diff --git a/ExampleApp/Shared/ExampleApp.swift b/ExampleApp/Shared/ExampleApp.swift index 87cae59..a265a5a 100644 --- a/ExampleApp/Shared/ExampleApp.swift +++ b/ExampleApp/Shared/ExampleApp.swift @@ -19,10 +19,10 @@ struct NavigatorDemoApp: App { var body: some Scene { WindowGroup { - let navigator = Navigator(rootScreen: Screens.rootScreen, viewFactory: MyViewFactory()) + let navigator = Navigator(rootScreen: ScreenID.rootScreen, viewFactory: AppViewFactory()) NavigationView.with(navigator) { - RootScreen(currentScreen: .rootScreen) + RootScreen(screenId: .rootScreen) } .accentColor(.black) diff --git a/ExampleApp/Shared/MyViewFactory.swift b/ExampleApp/Shared/MyViewFactory.swift index 99d9dbc..f43bc58 100644 --- a/ExampleApp/Shared/MyViewFactory.swift +++ b/ExampleApp/Shared/MyViewFactory.swift @@ -9,19 +9,19 @@ import Foundation import Navigator import SwiftUI -class MyViewFactory: ViewFactory { +class AppViewFactory: ViewFactory { @ViewBuilder - func makeView(screenType: ScreenWrapper) -> some View { + func makeView(screenType: ScreenWrapper) -> some View { switch screenType { - case .screenWrapper(let myScreen): - switch myScreen { + case .screenWrapper(let screenId): + switch screenId { case .greenScreen: - GreenScreen(currentScreen: myScreen!) + GreenScreen(screenId: screenId!) case .rootScreen: - RootScreen(currentScreen: myScreen!) + RootScreen(screenId: screenId!) case .blueScreen: - BlueScreen(currentScreen: myScreen!) + BlueScreen(screenId: screenId!) case .none: EmptyView() } @@ -29,7 +29,7 @@ class MyViewFactory: ViewFactory { } } -enum Screens: Hashable { +enum ScreenID: Hashable { case rootScreen case blueScreen(id: UUID = UUID()) case greenScreen(id: UUID = UUID()) diff --git a/ExampleApp/Shared/RootScreen.swift b/ExampleApp/Shared/RootScreen.swift index 2bdc377..aaadbd0 100644 --- a/ExampleApp/Shared/RootScreen.swift +++ b/ExampleApp/Shared/RootScreen.swift @@ -10,9 +10,9 @@ import Navigator struct RootScreen: ScreenView { - @EnvironmentObject var navigator: Navigator + @EnvironmentObject var navigator: Navigator @State var showNextScreen: Bool = false - var currentScreen: Screens + var screenId: ScreenID var body: some View { List { @@ -34,11 +34,11 @@ struct RootScreen: ScreenView { struct RootView_Preview: PreviewProvider { static var previews: some View { NavigationView { - RootScreen(currentScreen: .rootScreen) + RootScreen(screenId: .rootScreen) .environmentObject( Navigator( - rootScreen: Screens.rootScreen, - viewFactory: MyViewFactory() + rootScreen: ScreenID.rootScreen, + viewFactory: AppViewFactory() ) ) } diff --git a/ExampleApp/Shared/Util.swift b/ExampleApp/Shared/Util.swift index fb5797a..c81d4b5 100644 --- a/ExampleApp/Shared/Util.swift +++ b/ExampleApp/Shared/Util.swift @@ -13,19 +13,19 @@ public func delay(_ seconds: Double) async { try! await Task.sleep(nanoseconds: UInt64(seconds * 1_000_000_000)) } -func randomScreen() -> Screens { +func randomScreen() -> ScreenID { let randomFlag = Int.random(in: 0...1) switch randomFlag { - case 0: return Screens.greenScreen() - case 1: return Screens.blueScreen() + case 0: return ScreenID.greenScreen() + case 1: return ScreenID.blueScreen() default: fatalError() } } -extension Navigator where ScreenIdentifer == Screens { +extension Navigator where ScreenIdentifer == ScreenID { func popToFirstGreenScreenOrRoot(id: UUID) { - if let greenScreen: Screens = navStack.keys.elements.first(where: { - if case Screens.greenScreen(let detailID) = $0 { return detailID == id } + if let greenScreen: ScreenID = navStack.keys.elements.first(where: { + if case ScreenID.greenScreen(let detailID) = $0 { return detailID == id } return false }) { navStack[greenScreen]!.send(false) diff --git a/README.md b/README.md index ada9cb0..4df891d 100644 --- a/README.md +++ b/README.md @@ -24,9 +24,9 @@ navigate to other Views programatically in your app. ```swift struct DetailScreen: ScreenView { - @EnvironmentObject var navigator: Navigator + @EnvironmentObject var navigator: Navigator @State var showNextScreen: Bool = false - var currentScreen: Screens + var screenId: ScreenID var body: some View { Button("Next") { @@ -47,28 +47,28 @@ along with any needed values. Ensure that it conforms to `Hashable` ```swift -enum Screens: Hashable { +enum ScreenID: Hashable { case rootScreen case detailScreen(detailID: String) } ``` Then create a class that conforms to `ViewFactory` and -implement `makeView(screenType:)` so that the given `Screens` +implement `makeView(screenType:)` so that the given `ScreenID` enum returns the associated `View`. ```swift -class MyViewFactory: ViewFactory { +class AppViewFactory: ViewFactory { @ViewBuilder - func makeView(screenType: ScreenWrapper) -> some View { + func makeView(screenType: ScreenWrapper) -> some View { switch screenType { - case .screenWrapper(let myScreen): - switch myScreen { + case .screenWrapper(let screenId): + switch screenId { case .rootScreen: RootScreen() case .detailScreen: - DetailScreen(currentScreen: myScreen!) + DetailScreen(currentScreen: screenId!) case .none: // EmptyView is fine in the exhaustive case // as this is just a placeholder until the @@ -84,16 +84,17 @@ class MyViewFactory: ViewFactory { ### 2. Initialize Library In the `@main` app delcaration, where your root view -is the top level view in the navigation stack, inject -into the environment the necessary `Navigator` classes -and add the `NavigatorViewBinding` view modifier +is the top level view in the navigation stack (or wherever +you'd like to place a `NavigationView` with programatic navigation) +initialize the `NavigationView` using the `with(_:)` method to inject +your instance of `Navigator` ```swift @main struct MyApp: App { var body: some Scene { WindowGroup { - NavigationView.with(Navigator(rootScreen: Screens.rootScreen, viewFactory: MyViewFactory()) { + NavigationView.with(Navigator(rootScreen: ScreenID.rootScreen, viewFactory: AppViewFactory()) { RootScreen() } } @@ -108,9 +109,9 @@ and apply the `bindNavigation(_:binding:)` view modifier ```swift struct RootScreen: ScreenView { - @EnvironmentObject var navigator: Navigator + @EnvironmentObject var navigator: Navigator @State var showNextScreen: Bool = false - var currentScreen = .rootScreen + var screenId = .rootScreen var body: some View { List { @@ -126,9 +127,9 @@ struct RootScreen: ScreenView { ```swift struct DetailScreen: ScreenView { - @EnvironmentObject var navigator: Navigator + @EnvironmentObject var navigator: Navigator @State var showNextScreen: Bool = false - var currentScreen: Screens + var screenId: ScreenID var body: some View { VStack { @@ -149,14 +150,14 @@ struct DetailScreen: ScreenView { ``` Note that except for the `RootScreen` all other screens -should have an `id` in their `Screens` enum value in order +should have an `id` in their `ScreenID` enum value in order to differentiate same screens in the navigation stack. This can be in the form of a business logic id `detailID` or some deafult id that has no business logic but merely used to id the screen ```swift -enum Screens: Hashable { +enum ScreenID: Hashable { // ... case anotherScreen(id: UUID = UUID()) } @@ -170,9 +171,9 @@ View navigation ```swift struct RootScreen: ScreenView { - @EnvironmentObject var navigator: Navigator + @EnvironmentObject var navigator: Navigator @State var showNextScreen: Bool = false - var currentScreen = .rootScreen + var screenId = .rootScreen var body: some View { List { @@ -183,7 +184,7 @@ struct RootScreen: ScreenView { // This can also be called at an abstraction layer like a ViewModel - navigator.navigate(to: Screens.detailScreen(detailID: "detail-123")) + navigator.navigate(to: ScreenID.detailScreen(detailID: "detail-123")) } } .navigationTitle("Root Screen") @@ -194,9 +195,9 @@ struct RootScreen: ScreenView { ```swift struct DetailScreen: ScreenView { - @EnvironmentObject var navigator: Navigator + @EnvironmentObject var navigator: Navigator @State var showNextScreen: Bool = false - var currentScreen: Screens + var screenId: ScreenID var body: some View { VStack { @@ -220,7 +221,7 @@ struct DetailScreen: ScreenView { ## Advanced As stated previously, the `Navigator` class has the property `navStack` which keeps -a stack (OrderedDictionary) of the `ScreenView` associated `Screens` enums that are +a stack (OrderedDictionary) of the `ScreenView` associated `ScreenID` enum values that are currently active in the NavigationView. Clients can use it to execte custom navigation logic. ```swift @@ -233,15 +234,15 @@ currently active in the NavigationView. Clients can use it to execte custom navi As an example, we can use it create an extention funtion on `Navigator` called `popToDetailWithSpecificIdOrRoot(id: String)` ```swift -extension Navigator where ScreenIdentifer == Screens { +extension Navigator where ScreenIdentifer == ScreenID { func popToDetailWithSpecificIdOrRoot(id: String) { // Since all screens that have been pushed onto the NavigationView // are stored in the navStack as keys, we can simply search through // them for the specific detail screen with the called id - if let detailScreen: Screens = navStack.keys.elements.first(where: { - if case Screens.detailScreen(let detailID) = $0 { + if let detailScreen: ScreenID = navStack.keys.elements.first(where: { + if case ScreenID.detailScreen(let detailID) = $0 { return detailID == id } return false @@ -282,12 +283,12 @@ struct MyApp: App { // Keep reference to the navigator, either as a local property or in an abstraction // layer, such as an AppCoordinator - let navigator = Navigator(rootScreen: Screens.rootScreen, viewFactory: MyViewFactory()) + let navigator = Navigator(rootScreen: ScreenID.rootScreen, viewFactory: AppViewFactory()) var body: some Scene { WindowGroup { NavigationView.with(navigator) { - RootScreen(currentScreen: .rootScreen) + RootScreen() } .task { // mimic a system event, such as a notification diff --git a/Sources/Navigator/NavigationBindings.swift b/Sources/Navigator/NavigationBindings.swift index 6433576..e43681c 100644 --- a/Sources/Navigator/NavigationBindings.swift +++ b/Sources/Navigator/NavigationBindings.swift @@ -10,16 +10,16 @@ import SwiftUI /// navigate between ScreenViews of their app, dynamically. public struct NavigationBinding: ViewModifier { let navigation: Navigator - let currentScreen: ScreenIdentifer + let screenId: ScreenIdentifer var showNextScreenBinding: Binding public init( navigation: Navigator, - currentScreen: ScreenIdentifer, + screenId: ScreenIdentifer, showNextScreenBinding: Binding ) { self.navigation = navigation - self.currentScreen = currentScreen + self.screenId = screenId self.showNextScreenBinding = showNextScreenBinding } @@ -32,7 +32,7 @@ public struct NavigationBinding