⛵️ URLNavigator 提供了一种优雅的方式,通过 URL 来导航视图控制器。可以使用 URLNavigator.register(_:_:)
函数来映射 URL 模式。
URLNavigator 可以用于将 URL 模式映射到两种类型:URLNavigable
和 URLOpenHandler
。URLNavigable
是一种定义了自定义初始化方法的类型,而 URLOpenHandler
是一个可以执行的闭包。初始化方法和闭包都会接收一个 URL 和占位符的值。
URL 模式可以包含占位符。占位符将被来自 URL 的匹配值替换。使用 <
和 >
来创建占位符。占位符可以有以下类型:string
(默认)、int
、float
和 path
。
例如,myapp://user/<int:id>
匹配
myapp://user/123
myapp://user/87
但是它不匹配
myapp://user/devxoul
(期望 int)myapp://user/123/posts
(不同的 URL 结构)/user/devxoul
(缺少 scheme)URLNavigator 允许将视图控制器和 URL 打开处理器与 URL 模式进行映射。这是一个将 URL 模式与视图控制器和闭包进行映射的示例。每个闭包都有三个参数:url
、values
和 context
。
url
是从 push()
和 present()
传递过来的 URL。values
是一个包含 URL 占位符键值对的字典。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 可以使用 URL 推入和呈现视图控制器,以及执行闭包。
提供 from
参数给 push()
以指定新的视图控制器将被推入的导航控制器。类似地,提供 from
参数给 present()
以指定新的视图控制器将被呈现的视图控制器。如果传递 nil
,这是默认值,当前应用程序的最顶层视图控制器将被用于推入或呈现视图控制器。
present()
接受一个额外的参数:wrap
。如果指定了一个 UINavigationController
类,新的视图控制器将被该类包装。默认值为 nil
。
Navigator.push("myapp://user/123")
Navigator.present("myapp://post/54321", wrap: UINavigationController.self)
Navigator.open("myapp://alert?title=Hello&message=World")
URLNavigator 官方仅支持 CocoaPods。
Podfile
pod 'URLNavigator'
您可以在这里找到一个示例应用程序。
navigator://user/devxoul
。定义为全局常量
let navigator = Navigator()
class AppDelegate: UIResponder, UIApplicationDelegate {
// ...
}
注册到 IoC 容器
container.register(NavigatorProtocol.self) { _ in Navigator() } // Swinject
let navigator = container.resolve(NavigatorProtocol.self)!
从组合根注入依赖项。
我更喜欢使用单独的 URL 映射文件。
struct URLNavigationMap {
static func initialize(navigator: NavigatorProtocol) {
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,则可以使用 URL 打开您的应用程序。为了使用 URL 导航到视图控制器,您需要实现 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.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:_>
匹配
myapp://region/us-west-1
myapp://region/ap-northeast-2
myapp://region/eu-west-3
但是它不匹配
myapp://region/ca-central-1
有关更多信息,请参阅默认 URL 值转换器的实现。
URLNavigator 基于 MIT 许可证。 有关更多信息,请参见 LICENSE 文件。