Skip to content

Latest commit

 

History

History
228 lines (187 loc) · 6.68 KB

README.md

File metadata and controls

228 lines (187 loc) · 6.68 KB

DreemNav

Swift

🎱 Developer friendly library designed to easily setup navigation through your SwiftUI app.

Contents

Introduction

DreemNav is an iOS UIKit navigation wrapper designed for SwiftUI.

As the native navigation component provided by SwiftUI lacked legacys animation, this library has been built to wrap the old good UIKit system and provide it through a SwiftUI interface and a developer friendly way to implement it.

Installation

  • Using CocoaPods:

    pod 'DreemNav'
  • Using Swift Package Manager:

    import PackageDescription
    
    let package = Package(
        name: "YourPackage",
        dependencies: [
            .package(url: "https://github.com/Dreem-Organization/dreem-ios-navigation", from: "1.x.x")
      ]
    )

Public architecture

NavController

Which is the part of the Navigation that orchestrate how screens are displayed. It holds public methods that will be useful to perform navigation through the app.

NavGraphBuilder

Which defines how to build screens of the app. It is declared once into the NavController parameters.

NavHost

This is the View holder where each screen declared into the NavGraphBuilder will be displayed.

Initialization

Let admit that you have four different screens :

  • Splash
  • Home
  • FirstName
  • LastName

First of all you have to set up a NavController that takes as parameters :

  1. root: The name of the first screen that you want to be displayed.
  2. builder: The way above screens will be built
@main
struct SampleApp: App {
    @ObservedObject var controller =
        NavController(
            root: "Splash"
        ) { graph in
            graph.screen("Splash") { SplashScreen() }
            graph.screen("Home") { HomeScreen() }
            graph.screen("FirstName") { FirstNameScreen() }
            graph.screen("LastName") { navParams in
                let firstName = navParams["first_name"] as! String
                LastNameScreen(firstName: firstName)
            }
        }
    ...
}

As you can see in the above example, we have told to the NavController how to initialize Four named screens. "LastName" builder is particular as it takes a navParam as entry parameter, which is a [String: Any] that holds data passed through a push method call - see push section.

Now that we have defined how our screens will be built, we must pass the NavController to the NavHost in order to display those screens.

@main
struct SampleApp: App {
    var body: some Scene {
        WindowGroup {
            NavHost(controller: controller)
        }
    }
}

And ... Thats it ! Everything is ready, you can now manage the navigation using your newly set navControllers public methods from anywhere in your app !

Warning In order to manipulate the navigation properly, you have to use the same instance of the NavController that you have passed to NavHost as parameter.

Consider using an DI library such as Swinject Or a singleton design pattern.

NavController methods summary

push

Use this method display a new screen. You can pass arguments through the screen pushed :

private class FirstNameViewModel: ObservableObject {
    private var controller: NavController = resolve()
    @Published private(set) var firstName: String = ""

    func setFirstName(value: String) { self.firstName = value }
    
    func goToLastName() {
        controller.push(
            screenName: "LastName",
            arguments: ["first_name": firstName]
        )
    }
}

Those arguments are handled into the NavGraphBuilder :

@main
struct SampleApp: App {
    @ObservedObject var controller =
        NavController(
            root: ...
        ) { graph in
            ...
            graph.screen("LastName") { navParams in
            //HERE
                let firstName = navParams["first_name"] as! String
                LastNameScreen(firstName: firstName)
            }
        }
    ...
}

pop

Use this method to remove screens from the stack. You can pass arguments to the screen that will be displayed back and chose a specific screen that you want to be at the top of the stack :

private class LastNameViewModel: ObservableObject {
    private var controller: NavController = resolve()
    @Published private(set) var lastName: String = ""
    private(set) var firstName: String = ""
    
    func backToFirstName() { controller.pop() } //Simple pop.
    
    func backToHome() {
        controller.pop( //Pop with arguments and specific destination.
            to: "Home",
            arguments: [
                "first_name": firstName,
                "last_name": lastName,
            ]
        )
    }
}

Those arguments are handled into the place where you are navigating back (see setOnNavigateBack)

setOnNavigateBack

Allows you to define how the app should handle arguments on a pop navigation :

private class HomeViewModel: ObservableObject 
    private var controller: NavController = resolve()
    @Published private(set) var firstName: String = ""
    @Published private(set) var lastName: String = ""
    
    private init() {
        controller.setOnNavigateBack { args in
            if let firstName = args["first_name"] as? String, !firstName.isEmpty { //HERE
                self.firstName = firstName
            }
            
            if let lastName = args["last_name"] as? String, !lastName.isEmpty { //AND HERE
                self.lastName = lastName
            }
        }
    }
...
}

setNewRoot

Clear the entire screen's stack and set a new screen as root.

private class HomeViewModel: ObservableObject {
    private var controller: NavController = resolve()
    
    func goToAuth() {
        controller.setNewRoot(screenName: "Login")
    }
}

Sample

You can find a sample HERE

License

DreemNav is under MIT license. See the LICENSE file for more info.