DeeplinkNavigator
devxoul,本项目基于他/她的努力并结合启发而成。
感谢DeeplinkNavigator 可以用于将 URL 模式映射为两种类型:DeeplinkNavigable
和 DeeplinkOpenHandler
。其中 DeeplinkNavigable
是一个协议,包含两个子协议:StoryboardNavigable
和 InitNavigable
(viewController 的默认初始化方法)。DeeplinkOpenHandler
是一个可以被调用的闭包,它接收一个 URL 和一些占位符值。
开始使用
1. 映射 URL 模式
URL 模式可以包含占位符。占位符将被从 URL 中匹配的值替换。使用 <
和 >
来创建占位符。占位符可以有类型:string
(默认),int
,float
和 path
。
以下是一个使用视图控制器和闭包来映射 URL 模式的例子。视图控制器应该遵守 DeeplinkNavigable
协议才能通过 URL 模式进行映射。
Navigator.map("myapp://user/<int:id>", UserViewController.self)
Navigator.map("myapp://post/<title>", PostViewController.self)
Navigator.map("myapp://alert") { url, values in
print(url.queryParameters["title"])
print(url.queryParameters["message"])
return true
}
注意:全局常量
Navigator
是DeeplinkNavigator.default
的快捷方式。
2. 推送、呈现和打开 URL
DeeplinkNavigator 可以使用 URL 推送和呈现视图控制器以及执行闭包。
为 push()
方法提供 from
参数来指定新视图控制器将被推入的 DeeplinkPushable
。同样,为 present()
方法提供 from
参数来指定新视图控制器将被呈现的 DeeplinkPresentable
。如果传递 nil
(默认值),则使用当前应用的最顶层视图控制器来推送或呈现视图控制器。
present()
还有一个额外的参数:wrap
。如果指定为 true
,则新视图控制器将被包裹在一个 UINavigationController
中。默认值是 false
。
Navigator.push("myapp://user/123")
Navigator.present("myapp://post/54321", wrap: true)
Navigator.open("myapp://alert?title=Hello&message=World")
3. 实现 DeeplinkNavigable
视图控制器应该遵守 DeeplinkNavigable
协议以将其映射到 URL。一个 DeeplinkNavigable
协议定义了两个初始化协议:StoryboardNavigable
和 InitNavigable
,参数 navigation
包含了 url
、values
、mappingContext
和 navigationContext
作为属性。
url
属性是从 DeeplinkNavigator.push()
和 DeeplinkNavigator.present()
传来的 URL。参数 values
是一个包含 URL 占位符键和值的字典。mappingContext
是从 map()
函数传递的上下文。navigationContext
是一个包含从 push()
或 present()
传递的额外值的字典。
final class StoryboardViewController: UIViewController {
}
extension StoryboardViewController: StoryboardNavigable {
static func viewControllerFromStoryBoard(navigation: DeeplinkNavigation) -> UIViewController? {
let sb = UIStoryboard(name: "Main", bundle: nil)
let vc = sb.instantiateViewController(withIdentifier: String(describing: self))
return vc
}
}
final class XibViewController: UIViewController {
}
extension XibViewController: InitNavigable {
convenience init?(navigation: DeeplinkNavigation) {
self.init()
}
}
final class InitViewController: UIViewController {
}
extension InitViewController: InitNavigable {
convenience init?(navigation: DeeplinkNavigation) {
self.init()
}
}
安装
pod 'DeeplinkNavigator'
github "LawrenceHan/DeeplinkNavigator"
技巧和窍门
URL映射位置
我更倾向于使用独立的URL映射文件。
struct NavigationMap {
static func initialize() {
Navigator.map("myapp://user/<int:id>", UserViewController.self)
Navigator.map("myapp://post/<title>", PostViewController.self)
Navigator.map("myapp://alert") { url, values in
print(url.queryParameters["title"])
print(url.queryParameters["message"])
self.someUtilityMethod()
return true
}
}
private static func someUtilityMethod() {
print("This method is really useful")
}
}
然后在AppDelegate的application:didFinishLaunchingWithOptions:
中调用initialize()
。
@UIApplicationMain
final class AppDelegate: UIResponder, UIApplicationDelegate {
func application(
_ application: UIApplication,
didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?
) -> Bool {
// Navigator
URLNavigationMap.initialize()
// Do something else...
}
}
将UIView传递给push()或present()
有时你可能有一个多重窗口的情况,你可以传递一个UIView对象,让DeeplinkNavigator为你找到正确的UINavigationController
或UIViewController
。
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
let cell = tableView.cellForRow(at: indexPath)
Navigator.push("myapp://test", from: cell, animated: true)
}
使用pushOrPopTo扩展
你想push一个viewController,但又希望如果它已存在的话,弹出该viewController。
Navigator.popTo("myapp://user")
Navigator.pushOrPopTo("myapp://user/10")
实现AppDelegate启动选项URL
如果已注册自定义方案,则可以打开应用并使用URL。为了能够使用URL导航到viewController,需要实现application:didFinishLaunchingWithOptions:
方法。
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.push(url)
}
}
return true
}
实现 AppDelegate 打开 URL 方法
您可能需要实现自定义 URL 打开处理程序。以下是一个使用 DeeplinkNavigator 与其他 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
}
// DeeplinkNavigator Handler
if Navigator.open(url) {
return true
}
// DeeplinkNavigator View Controller
if Navigator.present(url, wrap: true) != nil {
return true
}
return false
}
设置默认方案
将 scheme
属性设置在 DeeplinkNavigator
实例上,可取消每个 URL 中的方案。
Navigator.scheme = "myapp"
Navigator.map("/user/<int:id>", UserViewController.self)
Navigator.push("/user/10")
这完全等同于
Navigator.map("myapp://user/<int:id>", UserViewController.self)
Navigator.push("myapp://user/10")
设置 scheme
属性不会影响已经具有方案的 URL。
Navigator.scheme = "myapp"
Navigator.map("/user/<int:id>", UserViewController.self) // `myapp://user/<int:id>`
Navigator.map("http://<path>", MyWebViewController.self) // `http://<path>`
在映射时传递上下文
let context = Foo()
Navigator.map("myapp://user/10", UserViewController.self, context: context)
在推送或显示时传递额外值
let context: [AnyHashable: Any] = [
"fromViewController": self
]
Navigator.push("myapp://user/10", context: context)
Navigator.present("myapp://user/10", context: context)
许可协议
DeeplinkNavigator 使用 MIT 许可证。有关更多信息,请参阅 LICENSE 文件。