⛵️ URLNavigator 是 Swfit 下一個優雅的 URL 路由。它提供了經過 URL 導航到 view controller 的方式。URL 參數的對應關係經過 URLNavigator.register(_:_:)
方法進行設置。git
URLNavigator 提供了兩種方法來設置 URL 參數的對應關係:URLNavigable
和 URLOpenHandler
。URLNavigable
經過自定義的初始化方法進行設置,URLOpenHandler
經過一個可執行的閉包進行設置。初始化方法和閉包都接受一個 URL 和佔位符值。github
URL 模式能夠包含多個佔位符。佔位符將會被匹配的 URL 中的值替換。使用 <
和 >
來設置佔位符。佔位符的類型能夠設置爲:string
(默認), int
, float
, 和 path
。swift
例如,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)URLNavigator 經過 URL 模式來匹配 view controllers 和 URL 的打開操做。下面是使用 view controller 和閉包映射 URL 模式的示例。每一個閉包有三個參數:url
, values
和 context
。app
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
}
複製代碼
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 包住。這個參數的默認值爲 nil
。url
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'
複製代碼
示例程序 點擊查看.
navigator://user/devxoul
。定義一個全局的常量:
let navigator = Navigator()
class AppDelegate: UIResponder, UIApplicationDelegate {
// ...
}
複製代碼
向 IoC 容器內註冊
container.register(NavigatorType.self) { _ in Navigator() } // Swinject
let navigator = container.resolve(NavigatorType.self)!
複製代碼
在合成根部註冊依賴
Inject dependency from a composition root.
做者喜歡將 URL 映射關係放到單獨的文件中。
struct URLNavigationMap {
static func initialize(navigator: NavigatorType) {
navigator.register("myapp://user/<int:id>") { ... }
navigator.register("myapp://post/<title>") { ... }
navigator.handle("myapp://alert") { ... }
}
}
複製代碼
而後在 AppDelegate
的 application: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...
}
}
複製代碼
若是註冊了自定義的 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
}
複製代碼
你可能須要實現自定義的 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
}
複製代碼
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 值的轉化器。
例如,佔位符 <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 值的默認轉換器的實現方式