Swfit URLNavigator 框架

⛵️ URLNavigator 是 Swfit 下一個優雅的 URL 路由。它提供了經過 URL 導航到 view controller 的方式。URL 參數的對應關係經過 URLNavigator.register(_:_:) 方法進行設置。git

URLNavigator 提供了兩種方法來設置 URL 參數的對應關係:URLNavigableURLOpenHandlerURLNavigable 經過自定義的初始化方法進行設置,URLOpenHandler 經過一個可執行的閉包進行設置。初始化方法和閉包都接受一個 URL 和佔位符值。github

開始

1. 理解 URL 模式

URL 模式能夠包含多個佔位符。佔位符將會被匹配的 URL 中的值替換。使用 <> 來設置佔位符。佔位符的類型能夠設置爲:string(默認), int, float, 和 pathswift

例如,myapp://user/<int:id> 將會和下面的 URL 匹配:ruby

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

可是,沒法和下面的 URL 配置:閉包

  • myapp://user/devxoul (類型錯誤,須要 int)
  • myapp://user/123/posts (url 的結構不匹配))
  • /user/devxoul (丟失 scheme)

2. View Controllers 和 URL 打開操做的匹配

URLNavigator 經過 URL 模式來匹配 view controllers 和 URL 的打開操做。下面是使用 view controller 和閉包映射 URL 模式的示例。每一個閉包有三個參數:url, valuescontextapp

  • url 是經過 push() 或者 present() 傳遞的 URL 參數.
  • values 是一個包含 URL 佔位符的 keys 和 values 的字典.
  • context 是經過 push(), present()open() 傳遞的額外值的字典。
let navigator = Navigator()

// 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
}
複製代碼

3. 彈出方式(Pushing, Presenting)與操做 URLs

URLNavigator 能夠經過執行一個帶 URLs 參數的閉包來 push 或者 presenet 對應的 view controllers。post

push() 方法中,經過指定 from 參數能夠設置彈出新 view controller 的 navigation controller。一樣,在 present() 方法中,經過指定 from 參數能夠設置彈出新 view controller 的 view controller。若是設置爲 nil,也就是默認值,當前應用程序最頂層的 view controller 將會被用來 push 或 present 新的 view controller。ui

present() 還有一個額外的參數 wrap。若是將這個參數設置爲 UINavigationController 類型,則會用這種類型的導航控制器把要彈出的新的 view controller 包住。這個參數的默認值爲 nilurl

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

Navigator.open("myapp://alert?title=Hello&message=World")
複製代碼

安裝

官方僅提供了經過 CocoaPods 安裝的方式。spa

Podfile

pod 'URLNavigator'
複製代碼

示例

示例程序 點擊查看.

  1. 編譯並安裝示例 app。
  2. 打開 Safari app。
  3. 在 URL 欄輸入 navigator://user/devxoul
  4. 示例程序將會被加載。

提示和竅門

初始化 Navigator 實例的時機

  1. 定義一個全局的常量:

    let navigator = Navigator()
    
    class AppDelegate: UIResponder, UIApplicationDelegate {
      // ...
    }
    複製代碼
  2. 向 IoC 容器內註冊

    container.register(NavigatorType.self) { _ in Navigator() } // Swinject
    let navigator = container.resolve(NavigatorType.self)!
    複製代碼
  3. 在合成根部註冊依賴

Inject dependency from a composition root.

關聯 URL 的時機

做者喜歡將 URL 映射關係放到單獨的文件中。

struct URLNavigationMap {
  static func initialize(navigator: NavigatorType) {
    navigator.register("myapp://user/<int:id>") { ... }
    navigator.register("myapp://post/<title>") { ... }
    navigator.handle("myapp://alert") { ... }
  }
}
複製代碼

而後在 AppDelegateapplication:didFinishLaunchingWithOptions: 方法中調用 initialize()

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

實現 AppDelegate 啓動選項 URL

若是註冊了自定義的 schemes,就能夠經過 URLs 來啓動咱們的 app 了。能夠經過實現 application:didFinishLaunchingWithOptions: 方法來實現經過 URLs 導航到指定的 view controller。

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

複製代碼

實現 AppDelegate 打開 URL 的方法

你可能須要實現自定義的 URL 打開處理程序。下面是一個使用 URLNavigator 和其餘 URL 打開處理程序的示例:

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
}
複製代碼

在 Pushing, Presenting 或 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)
複製代碼

定義自定義 URL 值的轉換器

你能夠自定義給 URL 佔位符設置 URL 值的轉化器。

例如,佔位符 <region> 僅可以接受 ["us-west-1", "ap-northeast-2", "eu-west-3"] 中的字符串。若是傳入的參數不包含在這些值中,URL 的參數將沒法匹配。

下面是在 Navigator 實例的 [String: URLValueConverter] 字典中添加一個自定義的值轉換器。

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
  }
}
複製代碼

經過上面的代碼,myapp://region/<region:_> 將會匹配下面的 URL:

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

可是和下面的 URL 就沒法匹配了:

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

其餘相關信息,能夠查看 URL 值的默認轉換器的實現方式

相關文章
相關標籤/搜索