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