Swift 版的 JDRouter 完整实现及子模块调用流程,其中scanAndRegisterAllModules自动注册各个模块及其回调的逻辑,其实依然调用了objc相关方法。
1. JDRouter 核心实现
import Foundation
import UIKit
public typealias JDRouterHandler = ([String: Any], ((Any?) -> Void)?) -> Void
public typealias JDInterceptorBlock = (String, [String: Any]?) -> Bool
public final class JDRouter {
// MARK: - Singleton
public static let shared = JDRouter()
// MARK: - Properties
private var routeMap: [String: JDRouterHandler] = [:]
private var interceptors: [JDInterceptorBlock] = []
private let lock = DispatchQueue(label: "com.jd.router.lock")
private var autoRegisterEnabled = false
public var globalCompletion: ((String, TimeInterval) -> Void)?
// MARK: - Initialization
private init() {}
// MARK: - Route Registration
public func register(_ url: String, handler: @escaping JDRouterHandler) {
guard !url.isEmpty else { return }
lock.sync {
let normalizedURL = normalize(url)
routeMap[normalizedURL] = handler
}
}
// MARK: - Open URL
@discardableResult
public func open(_ url: String) -> Bool {
return open(url, params: nil, completion: nil)
}
@discardableResult
public func open(_ url: String, params: [String: Any]?) -> Bool {
return open(url, params: params, completion: nil)
}
@discardableResult
public func open(_ url: String, params: [String: Any]?, completion: ((Any?) -> Void)?) -> Bool {
let normalizedURL = normalize(url)
let startTime = CFAbsoluteTimeGetCurrent()
// Interceptor chain
for interceptor in interceptors {
if !interceptor(normalizedURL, params) {
print("[JDRouter] Intercepted: \(url)")
return false
}
}
// Find handler
var handler: JDRouterHandler?
lock.sync {
handler = routeMap[normalizedURL]
}
guard let routeHandler = handler else {
degradeToWebView(normalizedURL, params: params)
return false
}
// Merge parameters
let queryParams = parseQueryParameters(url: normalizedURL)
var finalParams = params ?? [:]
queryParams.forEach { key, value in
// Only add if not already in params to avoid overriding
if finalParams[key] == nil {
finalParams[key] = value
}
}
// Execute handler
routeHandler(finalParams) { result in
completion?(result)
// Performance monitoring
let endTime = CFAbsoluteTimeGetCurrent()
self.globalCompletion?(normalizedURL, endTime - startTime)
}
return true
}
// MARK: - Interceptor Management
public func addInterceptor(_ interceptor: @escaping JDInterceptorBlock) {
lock.sync {
interceptors.append(interceptor)
}
}
public func removeInterceptor(_ interceptor: @escaping JDInterceptorBlock) {
lock.sync {
if let index = interceptors.firstIndex(where: { $0 as AnyObject === interceptor as AnyObject }) {
interceptors.remove(at: index)
}
}
}
// MARK: - Auto Registration
public static func enableAutoRegister() {
shared.autoRegisterEnabled = true
performAutoRegistration()
}
private static func performAutoRegistration() {
// Only perform once
struct Token { static var onceToken = false }
guard !Token.onceToken else { return }
Token.onceToken = true
scanAndRegisterAllModules()
}
private static func scanAndRegisterAllModules() {
let expectedClassCount = objc_getClassList(nil, 0)
let allClasses = UnsafeMutablePointer<AnyClass?>.allocate(capacity: Int(expectedClassCount))
let autoreleasingAllClasses = AutoreleasingUnsafeMutablePointer<AnyClass?>(allClasses)
let actualClassCount = objc_getClassList(autoreleasingAllClasses, expectedClassCount)
for i in 0..<actualClassCount {
guard let cls = allClasses[Int(i)] else { continue }
let className = String(describing: cls)
// Skip system classes
if className.hasPrefix("NS") || className.hasPrefix("UI") || className.hasPrefix("_") {
continue
}
// Check for class methods
var methodCount: UInt32 = 0
guard let methodList = class_copyMethodList(object_getClass(cls), &methodCount) else { continue }
for j in 0..<Int(methodCount) {
let selector = method_getName(methodList[j])
let methodName = NSStringFromSelector(selector)
if methodName.hasPrefix("routerHandle_") {
let components = methodName.components(separatedBy: "_")
if components.count >= 3 {
let module = components[1]
let interface = components[2]
let url = "jd://\(module)/\(interface)"
registerDynamicRoute(for: cls, selector: selector, url: url)
}
}
}
free(methodList)
}
allClasses.deallocate()
}
private static func registerDynamicRoute(for cls: AnyClass, selector: Selector, url: String) {
shared.register(url) { params, completion in
guard cls.responds(to: selector) else {
print("[JDRouter] Class \(cls) doesn't respond to \(selector)")
return
}
let methodSignature = cls.methodSignature(for: selector)
// 方法签名应该有两个参数:第一个是self,第二个是_cmd,然后是我们传递的两个参数(params和callback)
if methodSignature?.numberOfArguments != 4 {
print("[JDRouter] Method argument count mismatch: \(selector)")
return
}
// 使用NSInvocation调用类方法
let invocation = NSInvocation(methodSignature: methodSignature!)
invocation.target = cls
invocation.selector = selector
// 设置参数: 第一个参数是self (target已经设置),第二个是_cmd (selector已经设置),然后是我们的两个参数
// 注意:参数索引从2开始(0和1已经被self和_cmd占用)
withUnsafePointer(to: params) { paramsPtr in
invocation.setArgument(paramsPtr, at: 2)
}
// 注意:由于completion是可选闭包,我们将其转换为Any?以便传递
let completionWrapper = completion as Any?
withUnsafePointer(to: completionWrapper) { completionPtr in
invocation.setArgument(completionPtr, at: 3)
}
invocation.invoke()
// 如果有返回值,则获取返回值
if methodSignature?.methodReturnLength ?? 0 > 0 {
var returnValue: Any?
invocation.getReturnValue(&returnValue)
// 如果有completion,则通过completion返回
completion?(returnValue)
}
}
}
// MARK: - Helper Methods
private func normalize(_ url: String) -> String {
return url.trimmingCharacters(in: .whitespaces).lowercased()
}
private func parseQueryParameters(url: String) -> [String: String] {
guard let urlComponents = URLComponents(string: url) else { return [:] }
guard let queryItems = urlComponents.queryItems else { return [:] }
var params: [String: String] = [:]
for item in queryItems {
if let value = item.value {
params[item.name] = value
}
}
return params
}
private func degradeToWebView(_ url: String, params: [String: Any]?) {
print("[JDRouter] Degrading to WebView: \(url)")
// 实际项目中打开H5页面
}
// MARK: - Debug Tools
public func printAllRoutes() {
lock.sync {
print("====== Registered Routes ======")
routeMap.keys.forEach { url in
print("URL: \(url)")
}
print("==============================")
}
}
public func canOpen(_ url: String) -> Bool {
let normalizedURL = normalize(url)
return lock.sync {
routeMap[normalizedURL] != nil
}
}
}
2. 子模块实现(商品模块)
import UIKit
// 模块接口声明宏
public func JDROUTER_EXTERN_METHOD(_ module: String, _ interface: String, _ params: [String: Any], _ callback: ((Any?) -> Void)?) {
let url = "jd://\(module)/\(interface)"
JDRouter.shared.open(url, params: params, completion: callback)
}
@objc public class JDProductModule: NSObject {
@objc public class func load() {
// 确保自动注册已启用
JDRouter.enableAutoRegister()
}
// 商品详情页实现
@objc public class func routerHandle_Product_detail(_ arg: [String: Any], callback: ((Any?) -> Void)?) -> Any? {
// 1. 参数解析
guard let productId = arg["id"] as? String else {
callback?(["status": "error", "message": "Missing product ID"])
return nil
}
// 2. 创建视图控制器
let vc = JDProductDetailViewController()
vc.productId = productId
vc.sourceFrom = arg["source"] as? String ?? "unknown"
// 3. 页面跳转
if let topVC = topViewController() {
if let nav = topVC.navigationController {
nav.pushViewController(vc, animated: true)
} else {
topVC.present(vc, animated: true, completion: nil)
}
}
// 4. 返回结果
let result: [String: Any] = [
"status": "success",
"viewController": vc,
"timestamp": Date().timeIntervalSince1970
]
callback?(result)
return result
}
// 获取顶层控制器
class func topViewController() -> UIViewController? {
guard let rootVC = UIApplication.shared.keyWindow?.rootViewController else { return nil }
return findTopViewController(rootVC)
}
class func findTopViewController(_ vc: UIViewController) -> UIViewController {
if let nav = vc as? UINavigationController, let topVC = nav.topViewController {
return findTopViewController(topVC)
} else if let tab = vc as? UITabBarController, let selectedVC = tab.selectedViewController {
return findTopViewController(selectedVC)
} else if let presented = vc.presentedViewController {
return findTopViewController(presented)
}
return vc
}
}
3. 拦截器实现(登录检查)
// AppDelegate.swift
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
// 添加登录拦截器
JDRouter.shared.addInterceptor { url, params -> Bool in
// 需要登录的页面路径
if url.contains("jd://product/detail") || url.contains("jd://order/create") {
if !UserManager.shared.isLogin {
print("[JDRouter] Login required, redirecting to login")
// 保存原始请求
let redirectInfo: [String: Any] = [
"url": url,
"params": params ?? [:]
]
UserDefaults.standard.set(redirectInfo, forKey: "redirectAfterLogin")
// 跳转到登录页
JDRouter.shared.open("jd://user/login")
return false // 中断原始跳转
}
}
return true
}
// 启用自动注册
JDRouter.enableAutoRegister()
return true
}
4. 调用商品模块的完整流程
func didSelectProduct(_ productId: String) {
let url = "jd://product/detail?id=\(productId)&source=home"
JDRouter.shared.open(url, params: ["promotion": "summer_sale", "position": selectedIndex]) { result in
if let resultDict = result as? [String: Any],
resultDict["status"] as? String == "success" {
print("✅ Successfully opened product page: \(productId)")
} else {
print("❌ Failed to open product page")
}
}
}
5. 完整调用时序图
完整调用时序图
6. 关键步骤解析
-
自动注册流程
自动注册流程
-
调用过程
调用过程
-
参数合并逻辑
URL参数: id=123&source=home 代码参数: ["promotion": "summer_sale"] 合并结果: [ "id": "123", "source": "home", "promotion": "summer_sale" ]
-
跨模块通信
// 支付结果回调示例 JDRouter.shared.open("jd://order/pay", params: ["order_id": "O2023001"]) { result in if let resultDict = result as? [String: Any] { if resultDict["status"] as? String == "success" { self.showSuccess("支付成功") } else { self.showError(resultDict["reason"] as? String) } } }
7. 高级功能扩展
- 动态路由更新
extension JDRouter {
public func updateRoutesFromServer(_ config: [String: Any]) {
guard let routes = config["routes"] as? [String] else { return }
lock.sync {
// 清空现有路由
routeMap.removeAll()
// 注册新路由
routes.forEach { url in
let normalizedURL = normalize(url)
routeMap[normalizedURL] = { [weak self] params, completion in
self?.openWebURL(url, params: params)
}
}
}
}
private func openWebURL(_ url: String, params: [String: Any]?) {
print("Opening web URL: \(url)")
// 实际项目中打开H5页面
}
}
- AOP 路由监控
// 在 AppDelegate 中配置
JDRouter.shared.globalCompletion = { url, duration in
Analytics.track(event: "router_performance", properties: [
"url": url,
"duration": duration,
"timestamp": Date().timeIntervalSince1970
])
if duration > 0.5 {
Crashlytics.log("Slow router: \(url) (\(duration)s)")
}
}
- 路由调试面板
class RouterDebugViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
JDRouter.shared.printAllRoutes()
}
func testRoute(_ url: String) {
if JDRouter.shared.canOpen(url) {
JDRouter.shared.open(url) { result in
print("Test result: \(result ?? "nil")")
}
} else {
print("Route not registered: \(url)")
}
}
}
8. 实际应用场景
- 普通页面跳转
// 打开商品详情页
JDRouter.shared.open("jd://product/detail?id=10086",
params: ["source": "recommend"])
- 服务调用
// 加入购物车
JDRouter.shared.open("jd://cart/add", params: [
"product_id": "P1001",
"quantity": 2,
"sku": "red_large"
])
- 带回调的操作
// 支付操作
JDRouter.shared.open("jd://order/pay", params: ["order_id": "O2023001"]) { result in
if let resultDict = result as? [String: Any] {
if resultDict["status"] as? String == "success" {
self.showSuccess("支付成功")
} else {
self.showError(resultDict["reason"] as? String)
}
}
}
总结
Swift 版 JDRouter 实现了以下核心功能:
-
路由注册机制
- 自动扫描
routerHandle_
前缀的类方法 - 动态注册路由表
- 支持手动注册
- 自动扫描
-
路由调用流程
- URL 标准化处理
- 拦截器链式执行
- 参数自动合并
- Completion 回调支持
-
商品模块调用流程
- 自动注册商品详情路由
- 登录拦截器检查
- 参数解析与页面跳转
- 结果返回
-
高级特性
- 动态路由更新
- AOP 性能监控
- 路由调试工具
- 安全降级机制
image.png
优势:
- 自动化注册:减少手动注册代码
- 安全调用:拦截器统一管理权限
- 灵活扩展:支持动态路由更新
- 性能监控:内置 AOP 监控系统
- 类型安全:Swift 强类型保障
此实现保持了 Objective-C 版本的核心功能,同时利用 Swift 的现代语法特性(泛型、闭包、协议等)进行了优化,更适合 Swift 项目使用。