URLNavigator是Swift版本的Router。
Router的主要作用是解耦。之前在各個ViewController間跳轉(zhuǎn),需要import ViewController,這樣就造成ViewController之間的依賴,也即耦合。通過router不需要再import ViewController。所有的只要import router,只依賴router這一個類,router里再去import 其他的ViewController,這樣,就達到我們說的解耦。
一個基本完善的router,我認為應(yīng)該有下面幾個核心功能:
- 跳轉(zhuǎn)
ViewController - 跳轉(zhuǎn)服務(wù)
- 回傳值
跳轉(zhuǎn)ViewController是基本功能,這里包括跳轉(zhuǎn)的時候,傳入?yún)?shù)。
對于跳轉(zhuǎn),我們需要做到通過一個字符串,來跳轉(zhuǎn)到我們想要的頁面,那么我們首先要做的是將字符串和對應(yīng)的頁面關(guān)聯(lián)起來,到時候,你給我這個字符串,我就知道你需要去哪個頁面。
在URLNavigator里有一個注冊方法,就是將字符串和需要跳轉(zhuǎn)的ViewController關(guān)聯(lián)起來。
navigator.register("navigator://user") { url, values, context in
return UserViewController()
}
更進一步,可以在字符串里把需要傳遞的參數(shù)也帶上
navigator.register("navigator://user/<username>") { url, values, context in
guard let username = values["username"] as? String else { return nil }
return UserViewController(navigator: navigator, username: username)
}
這樣不僅可以跳轉(zhuǎn)到關(guān)聯(lián)頁面,還能傳遞參數(shù)。
register里面做的事很簡單,就是用一個字典將字符串和和ViewController關(guān)聯(lián)起來。
public typealias URLPattern = String
private var viewControllerFactories = [URLPattern: ViewControllerFactory]()
open func register(_ pattern: URLPattern, _ factory: @escaping ViewControllerFactory) {
self.viewControllerFactories[pattern] = factory
}
將字符串作為字典的key,創(chuàng)建ViewController的閉包作為value,就這樣關(guān)聯(lián)了字符串和ViewController。
在調(diào)用的時候,再根據(jù)字符串找到相應(yīng)的閉包,得到ViewController,執(zhí)行跳轉(zhuǎn)動作。
open func viewController(for url: URLConvertible, context: Any? = nil) -> UIViewController? {
let urlPatterns = Array(self.viewControllerFactories.keys)
guard let match = self.matcher.match(url, from: urlPatterns) else { return nil }
guard let factory = self.viewControllerFactories[match.pattern] else { return nil }
return factory(url, match.values, context)
}
從viewControllerFactories字典里拿到factory,再執(zhí)行factory(url, match.values, context)。
這里的guard let match = self.matcher.match(url, from: urlPatterns) else { return nil }是拿到url里的參數(shù),這里面參數(shù)的傳入有一個自己定義的規(guī)則。URLMatcher.swift就是專門處理字符串的拆分,拿到參數(shù)。
然后
navigator.push("navigator://user/zhangsan")
navigator.present("navigator://user/zhangsan")
調(diào)用服務(wù),有時候,我們并不想跳到一個頁面,僅僅是想調(diào)用某個類里面的某個函數(shù)。
navigator.register("navigator://user/<username>") { url, values, context in
guard let username = values["username"] as? String else { return nil }
return UserViewController(navigator: navigator, username: username)
}
這個是上面的注冊代碼,我們只要修改一下就可以了,調(diào)用服務(wù),其他的操作一樣
navigator.register("navigator://user/<username>") { url, values, context in
guard let username = values["username"] as? String else { return nil }
//獲取UserViewController對象 userVC,調(diào)用方法
userVC.callFuc(username: username)
}
這個是我假想的一個方法。實際有一個和register類似的方法
private var handlerFactories = [URLPattern: URLOpenHandlerFactory]()
open func handle(_ pattern: URLPattern, _ factory: @escaping URLOpenHandlerFactory) {
self.handlerFactories[pattern] = factory
}
有一個保存閉包和字符串對應(yīng)關(guān)系的字典handlerFactories,不和register字典共用。
所以調(diào)用方法是
navigator.handle("navigator://user/<username>") { (url, values, context) -> Bool in
guard let username = values["username"] as? String else { return nil }
//獲取UserViewController對象 userVC,調(diào)用方法
userVC.callFuc(username: username)
return true
}
回傳值,有時候,我們跳轉(zhuǎn)到某個頁面,需要這個頁面執(zhí)行后,把相關(guān)結(jié)果返回。
目前,URLNavigator還沒有第三個功能。
總結(jié)一下:自己定義一個字符串規(guī)則,包含頁面信息和參數(shù)信息,然后將字符串和對應(yīng)的閉包關(guān)聯(lián)起來,閉包可以是創(chuàng)建相應(yīng)ViewController的操作,也可以是調(diào)用函數(shù)的操作,也可以是其他操作。在router通過字符串跳轉(zhuǎn)的時候,拿到字符串,解析出參數(shù),找到相應(yīng)的閉包,將參數(shù)傳給閉包執(zhí)行,執(zhí)行閉包得到的ViewController,拿去跳轉(zhuǎn)。