⛵️ URLNavigator is Swift’s next elegant URL route. It provides a way to navigate to the View Controller through the URL. The mapping of the URL parameters is set using the urlnavigator.register (_:_:) method.

URLNavigator provides two methods to set the mapping of URL parameters: URLNavigable and URLOpenHandler. URLNavigable is set through a custom initialization method, and URLOpenHandler is set through an executable closure. Both the initialization method and the closure accept a URL and a placeholder value.

start

1. Understand URL patterns

A URL pattern can contain multiple placeholders. The placeholder will be replaced with a value from the matched URL. Use < and > to set the placeholders. The types of placeholders can be set to string(default), int, float, and PATH.

For example, myapp://user/

will match the following URL:

  • myapp://user/123
  • myapp://user/87

However, cannot be configured with the following URL:

  • myapp://user/devxoul(Type error, int required)
  • myapp://user/123/posts(Url structure mismatch)
  • /user/devxoulLost (scheme)

2. View Controllers match URL open operations

URLNavigator matches view Controllers with the open action of the URL through the URL pattern. Here is an example of mapping URL patterns using View Controller and closures. Each closure takes three parameters: URL, values, and context.

  • urlIs through thepush()orpresent()The URL parameter passed.
  • valuesIs a dictionary containing keys and values with URL placeholders.
  • contextIs through thepush().present()open()A dictionary of the extra values passed.
let navigator = Navigator(a)// register view controllers
navigator.register("myapp://user/<int:id>") { url, values, context in
  guard let userID = values["id"] as? Int else { return nil }
  return UserViewController(userID: userID)
}
navigator.register("myapp://post/<title>") { url, values, context in
  return storyboard.instantiateViewController(withIdentifier: "PostViewController")}// register url open handlers
navigator.handle("myapp://alert") { url, values, context in
  let title = url.queryParameters["title"]
  let message = url.queryParameters["message"]
  presentAlertController(title: title, message: message)
  return true
}
Copy the code

3. Pushing, Presenting and manipulating URLs

URLNavigator can push or presenet’s view controllers by executing a closure with a url argument.

In the push() method, you can set the navigation controller that brings up the new View Controller by specifying the from parameter. Similarly, in the present() method, you can set the View controller that will pop up the new View Controller by specifying the from parameter. If it’s set to nil, which is the default, the top view controller in the current application is going to be used to push or present the new View controller.

Present () has an additional argument, wrap. If you set this parameter to type UINavigationController, the new View controller to be popup will be wrapped with that type of navigation controller. The default value of this argument is nil.

Navigator.push("myapp://user/123")
Navigator.present("myapp://post/54321", wrap: UINavigationController.self)

Navigator.open("myapp://alert? title=Hello&message=World")
Copy the code

The installation

Official only provided through the CocoaPods installation way.

Podfile

pod 'URLNavigator'
Copy the code

The sample

Sample program click to view.

  1. Compile and install the sample app.
  2. Open the Safari app.
  3. Enter in the URL barnavigator://user/devxoul.
  4. The sample program will be loaded.

Tips and tricks

When to initialize the Navigator instance

  1. Define a global constant:

    let navigator = Navigator(a)class AppDelegate: UIResponder.UIApplicationDelegate {
      // ...
    }
    Copy the code
  2. Register with the IoC container

    container.register(NavigatorType.self) { _ in Navigator()}// Swinject
    let navigator = container.resolve(NavigatorType.self)!
    Copy the code
  3. Register dependencies at the synthetic root

Inject dependency from a composition root.

The timing of the associated URL

The authors prefer to keep URL mappings in separate files.

struct URLNavigationMap {
  static func initialize(navigator: NavigatorType) {
    navigator.register("myapp://user/<int:id>") {... } navigator.register("myapp://post/<title>") {... } navigator.handle("myapp://alert") {... }}}Copy the code

Then the AppDelegate application: didFinishLaunchingWithOptions: approach the initialize ().

@UIApplicationMain
final class AppDelegate: UIResponder.UIApplicationDelegate {
  func application(
    _ application: UIApplication,
    didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?
  ) -> Bool {
    // Navigator
    URLNavigationMap.initialize(navigator: navigator)
    
    // Do something else...}}Copy the code

Implement the AppDelegate launch option URL

If we register our custom schemes, we can launch our app through URLs. Can be realized by the application: didFinishLaunchingWithOptions: ways to implement by URLs to navigate to the specified view controller.

func application(
  _ application: UIApplication,
  didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?
) -> Bool {
  // ...
  if leturl = launchOptions? [.url]as? URL {
    if let opened = navigator.open(url)
    if! opened { navigator.present(url) } }return true
}

Copy the code

Implement the AppDelegate method to open the URL

You may need to implement a custom URL opening handler. Here is an example of opening handlers using URLNavigator and other urls:

func application(_application: UIApplication, open url: URL, sourceApplication: String? , annotation: Any) -> Bool {
  // If you're using Facebook SDK
  let fb = FBSDKApplicationDelegate.sharedInstance()
  if fb.application(application, open: url, sourceApplication: sourceApplication, annotation: annotation) {
    return true
  }

  // URLNavigator Handler
  if navigator.open(url) {
    return true
  }

  // URLNavigator View Controller
  if navigator.present(url, wrap: UINavigationController.self) != nil {
    return true
  }

  return false
}
Copy the code

Introduce added value in Pushing, Presenting, or Opening

let context: [AnyHashable: Any] = [
  "fromViewController": self
]
Navigator.push("myapp://user/10", context: context)
Navigator.present("myapp://user/10", context: context)
Navigator.open("myapp://alert? title=Hi", context: context)
Copy the code

Define a converter for custom URL values

You can customize the converter to set URL values for URL placeholders.

For example, the placeholder

can only accept strings in [” US-west-1 “, “ap-northeast-2”, “eu-west-3”]. If the parameters passed in are not included in these values, the parameters for the URL will not match.

Here’s how to add a custom value converter to the [String: URLValueConverter] dictionary of the Navigator instance.

navigator.matcher.valueConverters["region"] = { pathComponents, index in
  let allowedRegions = ["us-west-1"."ap-northeast-2"."eu-west-3"]
  if allowedRegions.contains(pathComponents[index]) {
    return pathComponents[index]
  } else {
    return nil}}Copy the code

Myapp ://region/
<_>
will match the following URL:

  • myapp://region/us-west-1
  • myapp://region/ap-northeast-2
  • myapp://region/eu-west-3

But it doesn’t match the following URL:

  • myapp://region/ca-central-1

For additional information, see how the default converter for URL values is implemented