ReerRouter
仅限 iOS 的应用程序 URL 路由器。受 URLNavigator 启发。
示例应用程序
要运行示例项目,请克隆仓库,然后首先从 Example 目录运行 pod install
需求
至少需要 iOS 10.0 Xcode 13.2
安装
CocoaPods
ReerRouter可通过CocoaPods获取。要安装它,只需将以下行添加到您的Podfile中
pod 'ReerRouter'
Swift Package Manager
import PackageDescription
let package = Package(
name: "YOUR_PROJECT_NAME",
targets: [],
dependencies: [
.package(url: "https://github.com/reers/ReerRouter.git", from: "1.0.0")
]
)
接下来,将ReerRouter添加到您的目标依赖项中,如下所示
.target(
name: "YOUR_TARGET_NAME",
dependencies: ["ReerRouter",]
),
入门
Route.Key
1. 了解Route.Key
有两种模式。
模式1:Route.Key
表示URL的host
和path
/// myapp://example.com/over/there?name=phoenix#nose
/// \______/\_________/\_________/ \__________/ \__/
/// | | | | |
/// scheme host path queries fragment
/// \_________/
/// |
/// route key
模式1:为路由实例设置host
,使用path
作为Route.Key
。
/// myapp://example.com/over/there?name=phoenix#nose
/// \______/\_________/\_________/ \__________/ \__/
/// | | | | |
/// scheme host path queries fragment
/// |
/// |
/// route key
2. 注册路由
模式 1
现在 Route.Key
代表的是 URL 的 host
和 path
的组合。
- 注册一个动作。
Router.shared.registerAction(with: "abc_action") { _ in
print("action executed.")
}
- 通过类型和路由键注册视图控制器。
extension Route.Key {
static let userPage: Self = "user"
}
Router.shared.register(UserViewController.self, forKey: .userPage)
Router.shared.register(UserViewController.self, forKey: "user")
- 通过类型和路由键注册视图控制器。
Router.shared.registerPageClasses(with: ["preference": PreferenceViewController.self])
- 通过类型名和路由键注册视图控制器。
Router.shared.registerPageClasses(with: ["preference": "ReerRouter_Example.PreferenceViewController"])
模式 2
首先,你应该为路由实例设置 host
。
Router.shared.host = "phoenix.com"
现在 Route.Key
代表的是 URL 路径,然后所有注册方法都与 模式 1
相同。("path", "/path" 都支持。)
- 为视图控制器实现
Routable
。
class UserViewController: UIViewController, Routable {
var params: [String: Any]
required init?(param: Route.Param) {
self.params = param.allParams
super.init(nibName: nil, bundle: nil)
}
}
3. 执行路径动作。
Router.shared.executeAction(byKey: "abc_action")
// Mode 1.
Router.shared.open("myapp://abc_action")
// Mode 2.
Router.shared.open("myapp://phoenix.com/abc_action")
4. 打开视图控制器。
Router.shared.present(byKey: .userPage, embedIn: UINavigationController.self, userInfo: [
"name": "apple",
"id": "123123"
])
// Mode 1.
Router.shared.open("myapp://user?name=phoenix")
Router.shared.push("myapp://user?name=phoenix")
Router.shared.present("myapp://user?name=phoenix")
// Mode 2.
Router.shared.open("myapp://phoenix.com/user?name=phoenix")
Router.shared.push("myapp://phoenix.com/user?name=phoenix")
Router.shared.present("myapp://phoenix.com/user?name=phoenix")
5. 关于路由的应用代理。
extension RouteManager: RouterDelegate {
func router(_ router: Router, willOpenURL url: URL, userInfo: [String : Any]) -> URL? {
print("will open \(url)")
if let _ = url.absoluteString.range(of: "google") {
return URL(string: url.absoluteString + "&extra1=234244&extra2=afsfafasd")
} else if let _ = url.absoluteString.range(of: "bytedance"), !isUserLoggedIn() {
print("intercepted by delegate")
return nil
}
return url
}
func router(_ router: Router, didOpenURL url: URL, userInfo: [String : Any]) {
print("did open \(url) success")
}
func router(_ router: Router, didFailToOpenURL url: URL, userInfo: [String : Any]) {
print("did fail to open \(url)")
}
func router(_ router: Router, didFallbackToURL url: URL, userInfo: [String: Any]) {
print("did fallback to \(url)")
}
}
6. 降级方案
- 使用
route_fallback_url
键作为出错时的回退链接。
Router.shared.open("myapp://unregisteredKey?route_fallback_url=myapp%3A%2F%2Fuser%3Fname%3Di_am_fallback")
7. 重定向
- 实现
redirectURLWithRouteParam(_:)
方法,以将视图控制器重定向到新URL。
class PreferenceViewController: UIViewController, Routable {
required init?(param: Route.Param) {
super.init(nibName: nil, bundle: nil)
}
class func redirectURLWithRouteParam(_ param: Route.Param) -> URL? {
if let value = param.allParams["some_key"] as? String, value == "redirect" {
return URL(string: "myapp://new_preference")
}
return nil
}
}
8. 路由的全局实例(单例)。
public let AppRouter = Router.shared
AppRouter.open("myapp://user")
9. 当将要打开和已打开时的通知。
NotificationCenter.default.addObserver(
forName: Notification.Name.routeWillOpenURL,
object: nil,
queue: .main
) { notification in
if let param = notification.userInfo?[Route.notificationUserInfoKey] as? Route.Param {
print("notification: route will open \(param.sourceURL)")
}
}
NotificationCenter.default.addObserver(
forName: Notification.Name.routeDidOpenURL,
object: nil,
queue: .main
) { notification in
if let param = notification.userInfo?[Route.notificationUserInfoKey] as? Route.Param {
print("notification: route did open \(param.sourceURL)")
}
}
10. 自定义控制过渡段。
public typealias UserTransition = (
_ fromNavigationController: UINavigationController?,
_ fromViewController: UIViewController?,
_ toViewController: UIViewController
) -> Bool
public enum TransitionExecutor {
/// Transition will be handled by router automatically.
case router
/// Transition will be handled by user who invoke the router `push` or `present` method.
case user(UserTransition)
/// Transition will be handled by user who invoke the router `push` or `present` method.
case delegate
}
let transition: Route.UserTransition = { fromNavigationController, fromViewController, toViewController in
toViewController.transitioningDelegate = self.animator
toViewController.modalPresentationStyle = .currentContext
// Use the router found view controller directly, or just handle transition by yourself.
// fromViewController?.present(toViewController, animated: true)
self.present(toViewController, animated: true)
return true
}
AppRouter.present(user.urlString, transitionExecutor: .user(transition))
11. UIViewController的打开样式。
路由器打开控制器的方式所依赖的优先级如下。
`Router` instance property `preferredOpenStyle` <
`Routable` property `preferredOpenStyle` that UIViewController implemented <
The method you called. If you called `Router.push(...)`, the view controller will be pushed.
12. 禁止过渡动画。
- 使用
route_no_animation
键来禁止动画。
Router.shared.open("myapp://user?name=google&route_no_animation=1")
13. 外部拦截。
在某些特殊情况下拦截一个路由,返回 false 表示拦截 URL。
Router.shared.addInterceptor(forKey: .userPage) { (_) -> Bool in
print("intercepted user page")
return true
}
Router.shared.addInterceptor(forKey: .userPage) { (params) -> Bool in
print("intercepted user page")
if let name = params.allParams["name"] as? String, name == "google" {
print("intercepted user page success")
return false
}
return true
}
作者
phoenix, [邮箱保护]
许可证
ReerRouter 受 MIT 许可证许可。有关更多信息,请参阅 LICENSE 文件。