iOS 路由机制深度解析:URLRouter & Target-Action & Scheme

路由机制概述

为什么需要路由?


┌─────────────────────────────────────────────────────────────────┐
│                        传统页面跳转                              │
├─────────────────────────────────────────────────────────────────┤
│                                                                 │
│    ┌──────────────┐         直接依赖         ┌──────────────┐   │
│    │   首页模块    │ ─────────────────────→ │   商品模块    │   │
│    │              │  import ProductModule   │              │   │
│    │  HomeVC      │ ───────────────────────→│ ProductVC    │   │
│    └──────────────┘                         └──────────────┘   │
│           │                                        │           │
│           │         直接依赖                       │           │
│           └─────────────────────┐   ┌─────────────┘           │
│                                 ▼   ▼                          │
│                          ┌──────────────┐                      │
│                          │   订单模块    │                      │
│                          │   OrderVC    │                      │
│                          └──────────────┘                      │
│                                                                 │
│    问题:                                                       │
│    ❌ 模块间强耦合,形成网状依赖                                  │
│    ❌ 无法独立编译和测试                                         │
│    ❌ 代码改动影响范围大                                         │
│    ❌ 不支持动态化和外部跳转                                     │
└─────────────────────────────────────────────────────────────────┘

┌─────────────────────────────────────────────────────────────────┐
│                        路由解耦后                                │
├─────────────────────────────────────────────────────────────────┤
│                                                                 │
│    ┌──────────────┐                         ┌──────────────┐   │
│    │   首页模块    │                         │   商品模块    │   │
│    └──────┬───────┘                         └───────▲──────┘   │
│           │                                         │           │
│           │ router.navigate("/product/123")        │           │
│           │                                         │           │
│           ▼                                         │           │
│    ┌─────────────────────────────────────────────────────────┐ │
│    │                     路由中心                              │ │
│    │  ┌─────────────────────────────────────────────────┐    │ │
│    │  │  路由表:                                        │    │ │
│    │  │  /home         → HomeViewController             │    │ │
│    │  │  /product/:id  → ProductViewController          │    │ │
│    │  │  /order/:id    → OrderViewController            │    │ │
│    │  │  /cart         → CartViewController             │    │ │
│    │  └─────────────────────────────────────────────────┘    │ │
│    └─────────────────────────────────────────────────────────┘ │
│                              │                                  │
│                              ▼                                  │
│                       ┌──────────────┐                         │
│                       │   订单模块    │                         │
│                       └──────────────┘                         │
│                                                                 │
│    优势:                                                       │
│    ✅ 模块间完全解耦                                            │
│    ✅ 支持外部 DeepLink                                         │
│    ✅ 动态化配置                                                │
│    ✅ 统一拦截和处理                                            │
└─────────────────────────────────────────────────────────────────┘

主流路由方案对比


┌──────────────────────────────────────────────────────────────────────────┐
│                         iOS 主流路由方案对比                              │
├────────────────┬────────────────┬────────────────┬───────────────────────┤
│      方案       │     代表框架    │      优点       │         缺点         │
├────────────────┼────────────────┼────────────────┼───────────────────────┤
│   URL Router   │  JLRoutes      │ • 解耦彻底      │ • 参数传递受限        │
│                │  MGJRouter     │ • 支持DeepLink  │ • 编译期无类型检查    │
│                │  HHRouter      │ • 配置灵活      │ • 硬编码URL字符串     │
├────────────────┼────────────────┼────────────────┼───────────────────────┤
│  Target-Action │  CTMediator    │ • 无需注册      │ • 依赖runtime        │
│                │                │ • 延迟加载      │ • 方法名硬编码        │
│                │                │ • 原生调用      │ • 缺少编译检查        │
├────────────────┼────────────────┼────────────────┼───────────────────────┤
│ Protocol-Class │  BeeHive       │ • 类型安全      │ • 需要注册协议        │
│                │  Swinject      │ • 编译期检查    │ • 接口变更成本高      │
│                │                │ • IDE支持好    │ • 协议维护成本        │
├────────────────┼────────────────┼────────────────┼───────────────────────┤
│    混合方案     │  自研Router    │ • 各取所长      │ • 架构复杂度较高      │
│                │                │ • 适应不同场景  │ • 学习成本            │
└────────────────┴────────────────┴────────────────┴───────────────────────┘

URL Scheme 机制

URL Scheme 基础


/*
 ═══════════════════════════════════════════════════════════════════
                        URL Scheme 结构
 ═══════════════════════════════════════════════════════════════════
 
     myapp://product/detail?id=123&source=home
     ─────   ───────────── ─────────────────────
       │           │                │
    Scheme       Path            Query
    (协议)       (路径)         (查询参数)
 
 
 完整URL结构:
 ┌────────────────────────────────────────────────────────────────┐
 │  scheme://user:password@host:port/path?query=value#fragment   │
 └────────────────────────────────────────────────────────────────┘
 
 常见使用场景:
 ┌─────────────────────────────────────────────────────────────────┐
 │  场景                      │  示例URL                           │
 ├─────────────────────────────────────────────────────────────────┤
 │  App外部打开               │  myapp://home                       │
 │  Push通知跳转              │  myapp://order/123                  │
 │  第三方分享回调             │  myapp://share/callback?code=xxx    │
 │  广告投放追踪              │  myapp://product/456?utm_source=ad  │
 │  App间跳转                │  weixin://                          │
 │  Universal Links          │  https://app.example.com/product/1  │
 └─────────────────────────────────────────────────────────────────┘
 */

Info.plist 配置


<!-- Info.plist URL Scheme 配置 -->

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" 
    "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
    <!-- URL Types 配置 -->
    <key>CFBundleURLTypes</key>
    <array>
        <!-- 主 Scheme -->
        <dict>
            <key>CFBundleURLName</key>
            <string>com.yourcompany.myapp</string>
            <key>CFBundleURLSchemes</key>
            <array>
                <string>myapp</string>
            </array>
            <key>CFBundleTypeRole</key>
            <string>Editor</string>
        </dict>
        
        <!-- 微信回调 Scheme -->
        <dict>
            <key>CFBundleURLName</key>
            <string>weixin</string>
            <key>CFBundleURLSchemes</key>
            <array>
                <string>wx1234567890abcdef</string>
            </array>
        </dict>
        
        <!-- 支付宝回调 Scheme -->
        <dict>
            <key>CFBundleURLName</key>
            <string>alipay</string>
            <key>CFBundleURLSchemes</key>
            <array>
                <string>ap2021123456789012</string>
            </array>
        </dict>
    </array>
    
    <!-- 可以打开的外部 Scheme(iOS 9+需要白名单) -->
    <key>LSApplicationQueriesSchemes</key>
    <array>
        <string>weixin</string>
        <string>wechat</string>
        <string>alipay</string>
        <string>mqq</string>
        <string>weibo</string>
    </array>
</dict>
</plist>

Universal Links 配置


/*
 ═══════════════════════════════════════════════════════════════════
                      Universal Links
 ═══════════════════════════════════════════════════════════════════
 
 优势对比:
 ┌────────────────────┬─────────────────┬─────────────────────────┐
 │       特性          │  URL Scheme     │   Universal Links       │
 ├────────────────────┼─────────────────┼─────────────────────────┤
 │  唯一性            │  ❌ 可能冲突     │  ✅ 域名唯一            │
 │  回退网页          │  ❌ 不支持       │  ✅ 可打开网页          │
 │  微信等App内打开   │  ❌ 被屏蔽       │  ✅ 可直接打开          │
 │  安全性            │  ❌ 任何App可用  │  ✅ 域名验证            │
 │  配置复杂度        │  简单           │  需服务端配合           │
 └────────────────────┴─────────────────┴─────────────────────────┘
 */

// MARK: - apple-app-site-association 文件 (放在服务器 .well-known 目录)

/*
{
    "applinks": {
        "apps": [],
        "details": [
            {
                "appID": "TEAMID.com.yourcompany.myapp",
                "paths": [
                    "/product/*",
                    "/order/*",
                    "/user/*",
                    "/share/*",
                    "NOT /api/*",
                    "NOT /static/*"
                ]
            }
        ]
    },
    "webcredentials": {
        "apps": ["TEAMID.com.yourcompany.myapp"]
    }
}
*/

// MARK: - Entitlements 配置

/*
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" 
    "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
    <key>com.apple.developer.associated-domains</key>
    <array>
        <string>applinks:app.yourcompany.com</string>
        <string>applinks:www.yourcompany.com</string>
    </array>
</dict>
</plist>
*/

URL 处理入口


// MARK: - SceneDelegate (iOS 13+)

class SceneDelegate: UIResponder, UIWindowSceneDelegate {
    
    var window: UIWindow?
    
    // 冷启动时通过 URL 打开
    func scene(_ scene: UIScene, 
               willConnectTo session: UISceneSession, 
               options connectionOptions: UIScene.ConnectionOptions) {
        
        // 处理 URL Scheme
        if let url = connectionOptions.urlContexts.first?.url {
            handleIncomingURL(url, isLaunch: true)
        }
        
        // 处理 Universal Links
        if let userActivity = connectionOptions.userActivities.first,
           userActivity.activityType == NSUserActivityTypeBrowsingWeb,
           let url = userActivity.webpageURL {
            handleUniversalLink(url, isLaunch: true)
        }
    }
    
    // App 在后台时通过 URL 唤起
    func scene(_ scene: UIScene, openURLContexts URLContexts: Set<UIOpenURLContext>) {
        guard let url = URLContexts.first?.url else { return }
        handleIncomingURL(url, isLaunch: false)
    }
    
    // Universal Links
    func scene(_ scene: UIScene, continue userActivity: NSUserActivity) {
        guard userActivity.activityType == NSUserActivityTypeBrowsingWeb,
              let url = userActivity.webpageURL else {
            return
        }
        handleUniversalLink(url, isLaunch: false)
    }
    
    // MARK: - URL Handling
    
    private func handleIncomingURL(_ url: URL, isLaunch: Bool) {
        print("📥 [URL] Incoming: (url.absoluteString), isLaunch: (isLaunch)")
        
        // 检查是否是第三方回调
        if handleThirdPartyCallback(url) {
            return
        }
        
        // 使用路由系统处理
        DispatchQueue.main.asyncAfter(deadline: .now() + (isLaunch ? 0.5 : 0)) {
            URLRouter.shared.open(url: url, from: self.topViewController())
        }
    }
    
    private func handleUniversalLink(_ url: URL, isLaunch: Bool) {
        print("🔗 [UniversalLink] Incoming: (url.absoluteString)")
        
        // 转换为内部 URL 格式
        let internalURL = convertToInternalURL(url)
        handleIncomingURL(internalURL, isLaunch: isLaunch)
    }
    
    private func handleThirdPartyCallback(_ url: URL) -> Bool {
        let scheme = url.scheme ?? ""
        
        // 微信回调
        if scheme.hasPrefix("wx") {
            return WXApi.handleOpen(url, delegate: WXApiManager.shared)
        }
        
        // 支付宝回调
        if scheme.hasPrefix("ap") {
            AlipaySDK.defaultService().processOrder(withPaymentResult: url) { result in
                // 处理支付结果
            }
            return true
        }
        
        return false
    }
    
    private func convertToInternalURL(_ url: URL) -> URL {
        // https://app.yourcompany.com/product/123 -> myapp://product/123
        var components = URLComponents(url: url, resolvingAgainstBaseURL: false)
        components?.scheme = "myapp"
        components?.host = nil
        return components?.url ?? url
    }
    
    private func topViewController() -> UIViewController? {
        guard let rootVC = window?.rootViewController else { return nil }
        return findTopViewController(from: rootVC)
    }
    
    private func findTopViewController(from viewController: UIViewController) -> UIViewController {
        if let nav = viewController as? UINavigationController,
           let topVC = nav.topViewController {
            return findTopViewController(from: topVC)
        }
        if let tab = viewController as? UITabBarController,
           let selectedVC = tab.selectedViewController {
            return findTopViewController(from: selectedVC)
        }
        if let presentedVC = viewController.presentedViewController {
            return findTopViewController(from: presentedVC)
        }
        return viewController
    }
}

// MARK: - AppDelegate (iOS 12 及以下兼容)

@main
class AppDelegate: UIResponder, UIApplicationDelegate {
    
    var window: UIWindow?
    
    // iOS 12 及以下的 URL 处理
    func application(_ app: UIApplication, 
                    open url: URL, 
                    options: [UIApplication.OpenURLOptionsKey: Any] = [:]) -> Bool {
        print("📥 [URL] Open: (url.absoluteString)")
        
        // 获取来源App信息
        let sourceApp = options[.sourceApplication] as? String
        let annotation = options[.annotation]
        
        print("   Source App: (sourceApp ?? "unknown")")
        
        // 处理URL
        handleIncomingURL(url)
        return true
    }
    
    // Universal Links (iOS 12 及以下)
    func application(_ application: UIApplication, 
                    continue userActivity: NSUserActivity, 
                    restorationHandler: @escaping ([UIUserActivityRestoring]?) -> Void) -> Bool {
        guard userActivity.activityType == NSUserActivityTypeBrowsingWeb,
              let url = userActivity.webpageURL else {
            return false
        }
        handleUniversalLink(url)
        return true
    }
    
    private func handleIncomingURL(_ url: URL) {
        URLRouter.shared.open(url: url, from: nil)
    }
    
    private func handleUniversalLink(_ url: URL) {
        // 转换并处理
        handleIncomingURL(url)
    }
}

URLRouter 路由方案

完整 URLRouter 实现


// MARK: - URL路由核心实现

import UIKit
import Combine

// ═══════════════════════════════════════════════════════════════════
// MARK: - 路由协议定义
// ═══════════════════════════════════════════════════════════════════

/// 路由结果
public enum RouteResult {
    case viewController(UIViewController)
    case handler(() -> Void)
    case redirect(URL)
    case failed(RouteError)
}

/// 路由错误
public enum RouteError: Error, LocalizedError {
    case notFound(path: String)
    case invalidParameter(String)
    case requireLogin
    case accessDenied(reason: String)
    case custom(Error)
    
    public var errorDescription: String? {
        switch self {
        case .notFound(let path): return "路由未找到: (path)"
        case .invalidParameter(let msg): return "参数无效: (msg)"
        case .requireLogin: return "需要登录"
        case .accessDenied(let reason): return "访问被拒绝: (reason)"
        case .custom(let error): return error.localizedDescription
        }
    }
}

/// 导航类型
public enum NavigationType {
    case push           // Push导航
    case present        // 模态弹出
    case presentFullScreen  // 全屏模态
    case replace        // 替换当前页
    case root           // 设为根控制器
    case custom((UIViewController, UIViewController) -> Void)
}

/// 路由选项
public struct RouteOptions {
    public var navigationType: NavigationType
    public var animated: Bool
    public var completion: (() -> Void)?
    
    public init(navigationType: NavigationType = .push,
                animated: Bool = true,
                completion: (() -> Void)? = nil) {
        self.navigationType = navigationType
        self.animated = animated
        self.completion = completion
    }
    
    public static let `default` = RouteOptions()
    public static let present = RouteOptions(navigationType: .present)
    public static let presentFullScreen = RouteOptions(navigationType: .presentFullScreen)
}

/// 路由处理器协议
public protocol RouteHandler {
    func handle(url: URL, params: RouteParams) -> RouteResult
}

/// 路由拦截器协议
public protocol RouteInterceptor {
    /// 拦截器优先级(数字越大越先执行)
    var priority: Int { get }
    
    /// 是否拦截该路由
    func shouldIntercept(url: URL, params: RouteParams) -> Bool
    
    /// 处理拦截(返回nil表示继续路由,返回URL表示重定向)
    func intercept(url: URL, params: RouteParams, 
                   completion: @escaping (InterceptResult) -> Void)
}

/// 拦截结果
public enum InterceptResult {
    case `continue`              // 继续路由
    case redirect(URL)           // 重定向
    case reject(RouteError)      // 拒绝路由
    case handled                 // 已处理,不需要继续
}

extension RouteInterceptor {
    public var priority: Int { return 0 }
}

// ═══════════════════════════════════════════════════════════════════
// MARK: - 路由参数
// ═══════════════════════════════════════════════════════════════════

/// 路由参数容器
public struct RouteParams {
    /// URL中的路径参数 (如 /product/:id 中的 id)
    public var pathParams: [String: String] = [:]
    
    /// URL中的查询参数 (如 ?page=1&size=10)
    public var queryParams: [String: String] = [:]
    
    /// 额外传递的对象参数
    public var extra: [String: Any] = [:]
    
    /// 来源URL
    public var sourceURL: URL?
    
    /// 获取String参数
    public func string(_ key: String) -> String? {
        return pathParams[key] ?? queryParams[key] ?? extra[key] as? String
    }
    
    /// 获取Int参数
    public func int(_ key: String) -> Int? {
        if let value = string(key) {
            return Int(value)
        }
        return extra[key] as? Int
    }
    
    /// 获取Bool参数
    public func bool(_ key: String) -> Bool {
        if let value = string(key) {
            return ["true", "1", "yes"].contains(value.lowercased())
        }
        return extra[key] as? Bool ?? false
    }
    
    /// 获取任意类型参数
    public func get<T>(_ key: String) -> T? {
        return extra[key] as? T
    }
    
    /// 合并参数
    public mutating func merge(_ other: [String: Any]) {
        for (key, value) in other {
            if let stringValue = value as? String {
                queryParams[key] = stringValue
            } else {
                extra[key] = value
            }
        }
    }
}

// ═══════════════════════════════════════════════════════════════════
// MARK: - URLRouter 核心实现
// ═══════════════════════════════════════════════════════════════════

public final class URLRouter {
    
    public static let shared = URLRouter()
    
    // MARK: - Properties
    
    /// 路由表:path pattern -> handler
    private var routeMap: [String: (URL, RouteParams) -> RouteResult] = [:]
    
    /// 正则路由表(支持更复杂的匹配)
    private var regexRouteMap: [(NSRegularExpression, (URL, RouteParams) -> RouteResult)] = []
    
    /// 拦截器列表
    private var interceptors: [RouteInterceptor] = []
    
    /// 全局路由监听
    private var globalListeners: [(URL, RouteParams) -> Void] = []
    
    /// 404处理器
    private var notFoundHandler: ((URL) -> UIViewController)?
    
    /// 错误处理器
    private var errorHandler: ((RouteError, URL) -> Void)?
    
    /// URL Scheme
    private let scheme: String
    
    /// 线程安全锁
    private let lock = NSRecursiveLock()
    
    // MARK: - Initialization
    
    public init(scheme: String = "myapp") {
        self.scheme = scheme
    }
    
    // ═══════════════════════════════════════════════════════════════
    // MARK: - 路由注册
    // ═══════════════════════════════════════════════════════════════
    
    /// 注册页面路由(返回ViewController)
    @discardableResult
    public func register(
        _ pattern: String,
        handler: @escaping (URL, RouteParams) -> UIViewController?
    ) -> Self {
        let normalizedPattern = normalizePattern(pattern)
        
        lock.lock()
        defer { lock.unlock() }
        
        routeMap[normalizedPattern] = { url, params in
            if let vc = handler(url, params) {
                return .viewController(vc)
            }
            return .failed(.notFound(path: pattern))
        }
        
        print("🛣️ [Router] Registered: (normalizedPattern)")
        return self
    }
    
    /// 注册动作路由(执行闭包)
    @discardableResult
    public func registerAction(
        _ pattern: String,
        handler: @escaping (URL, RouteParams) -> Void
    ) -> Self {
        let normalizedPattern = normalizePattern(pattern)
        
        lock.lock()
        defer { lock.unlock() }
        
        routeMap[normalizedPattern] = { url, params in
            return .handler { handler(url, params) }
        }
        
        print("⚡ [Router] Registered Action: (normalizedPattern)")
        return self
    }
    
    /// 注册正则路由
    @discardableResult
    public func registerRegex(
        _ pattern: String,
        handler: @escaping (URL, RouteParams) -> UIViewController?
    ) -> Self {
        guard let regex = try? NSRegularExpression(pattern: pattern, options: []) else {
            print("❌ [Router] Invalid regex pattern: (pattern)")
            return self
        }
        
        lock.lock()
        defer { lock.unlock() }
        
        regexRouteMap.append((regex, { url, params in
            if let vc = handler(url, params) {
                return .viewController(vc)
            }
            return .failed(.notFound(path: pattern))
        }))
        
        print("🔤 [Router] Registered Regex: (pattern)")
        return self
    }
    
    /// 注册重定向
    @discardableResult
    public func registerRedirect(from source: String, to destination: String) -> Self {
        let normalizedSource = normalizePattern(source)
        
        lock.lock()
        defer { lock.unlock() }
        
        routeMap[normalizedSource] = { [weak self] url, params in
            guard let destURL = self?.buildURL(destination, params: params) else {
                return .failed(.notFound(path: destination))
            }
            return .redirect(destURL)
        }
        
        print("↪️ [Router] Registered Redirect: (source) -> (destination)")
        return self
    }
    
    /// 批量注册
    @discardableResult
    public func registerBatch(
        _ routes: [(String, (URL, RouteParams) -> UIViewController?)]
    ) -> Self {
        for (pattern, handler) in routes {
            register(pattern, handler: handler)
        }
        return self
    }
    
    // ═══════════════════════════════════════════════════════════════
    // MARK: - 路由导航
    // ═══════════════════════════════════════════════════════════════
    
    /// 通过URL字符串导航
    public func open(
        _ urlString: String,
        from sourceVC: UIViewController? = nil,
        options: RouteOptions = .default,
        extra: [String: Any]? = nil
    ) {
        guard let url = URL(string: urlString) else {
            print("❌ [Router] Invalid URL: (urlString)")
            return
        }
        open(url: url, from: sourceVC, options: options, extra: extra)
    }
    
    /// 通过URL对象导航
    public func open(
        url: URL,
        from sourceVC: UIViewController? = nil,
        options: RouteOptions = .default,
        extra: [String: Any]? = nil
    ) {
        // 解析参数
        var params = parseParams(from: url)
        if let extra = extra {
            params.merge(extra)
        }
        params.sourceURL = url
        
        print("🔍 [Router] Opening: (url.absoluteString)")
        
        // 通知全局监听器
        notifyListeners(url: url, params: params)
        
        // 执行拦截器链
        runInterceptors(url: url, params: params) { [weak self] result in
            switch result {
            case .continue:
                self?.performRouting(url: url, params: params, 
                                    from: sourceVC, options: options)
                
            case .redirect(let newURL):
                self?.open(url: newURL, from: sourceVC, options: options)
                
            case .reject(let error):
                self?.handleError(error, for: url)
                
            case .handled:
                print("✅ [Router] Handled by interceptor")
            }
        }
    }
    
    /// 获取目标ViewController(不进行导航)
    public func viewController(for urlString: String, 
                               extra: [String: Any]? = nil) -> UIViewController? {
        guard let url = URL(string: urlString) else { return nil }
        return viewController(for: url, extra: extra)
    }
    
    public func viewController(for url: URL, 
                               extra: [String: Any]? = nil) -> UIViewController? {
        var params = parseParams(from: url)
        if let extra = extra {
            params.merge(extra)
        }
        
        let result = matchRoute(url: url, params: params)
        
        switch result {
        case .viewController(let vc):
            return vc
        default:
            return nil
        }
    }
    
    /// 检查URL是否可以被处理
    public func canOpen(_ urlString: String) -> Bool {
        guard let url = URL(string: urlString) else { return false }
        return canOpen(url: url)
    }
    
    public func canOpen(url: URL) -> Bool {
        let params = parseParams(from: url)
        let result = matchRoute(url: url, params: params)
        
        if case .failed = result {
            return false
        }
        return true
    }
    
    // ═══════════════════════════════════════════════════════════════
    // MARK: - 拦截器管理
    // ═══════════════════════════════════════════════════════════════
    
    /// 添加拦截器
    public func addInterceptor(_ interceptor: RouteInterceptor) {
        lock.lock()
        defer { lock.unlock() }
        
        interceptors.append(interceptor)
        // 按优先级排序
        interceptors.sort { $0.priority > $1.priority }
    }
    
    /// 移除拦截器
    public func removeInterceptor<T: RouteInterceptor>(_ type: T.Type) {
        lock.lock()
        defer { lock.unlock() }
        
        interceptors.removeAll { $0 is T }
    }
    
    // ═══════════════════════════════════════════════════════════════
    // MARK: - 配置
    // ═══════════════════════════════════════════════════════════════
    
    /// 设置404处理器
    public func setNotFoundHandler(_ handler: @escaping (URL) -> UIViewController) {
        notFoundHandler = handler
    }
    
    /// 设置错误处理器
    public func setErrorHandler(_ handler: @escaping (RouteError, URL) -> Void) {
        errorHandler = handler
    }
    
    /// 添加全局路由监听
    public func addListener(_ listener: @escaping (URL, RouteParams) -> Void) {
        lock.lock()
        defer { lock.unlock() }
        globalListeners.append(listener)
    }
    
    // ═══════════════════════════════════════════════════════════════
    // MARK: - Private Methods
    // ═══════════════════════════════════════════════════════════════
    
    /// 标准化路由模式
    private func normalizePattern(_ pattern: String) -> String {
        var result = pattern.lowercased()
        if !result.hasPrefix("/") {
            result = "/" + result
        }
        if result.hasSuffix("/") && result.count > 1 {
            result.removeLast()
        }
        return result
    }
    
    /// 解析URL参数
    private func parseParams(from url: URL) -> RouteParams {
        var params = RouteParams()
        
        // 解析Query参数
        if let components = URLComponents(url: url, resolvingAgainstBaseURL: false),
           let queryItems = components.queryItems {
            for item in queryItems {
                params.queryParams[item.name] = item.value ?? ""
            }
        }
        
        return params
    }
    
    /// 匹配路由
    private func matchRoute(url: URL, params: RouteParams) -> RouteResult {
        let path = normalizePattern(url.path)
        var mutableParams = params
        
        // 1. 精确匹配
        if let handler = routeMap[path] {
            return handler(url, mutableParams)
        }
        
        // 2. 模式匹配 (支持 :param 占位符)
        for (pattern, handler) in routeMap {
            if let pathParams = matchPattern(path: path, pattern: pattern) {
                mutableParams.pathParams = pathParams
                return handler(url, mutableParams)
            }
        }
        
        // 3. 正则匹配
        for (regex, handler) in regexRouteMap {
            let range = NSRange(path.startIndex..., in: path)
            if let match = regex.firstMatch(in: path, options: [], range: range) {
                // 提取正则捕获组
                for i in 1..<match.numberOfRanges {
                    if let range = Range(match.range(at: i), in: path) {
                        mutableParams.pathParams["group(i)"] = String(path[range])
                    }
                }
                return handler(url, mutableParams)
            }
        }
        
        return .failed(.notFound(path: path))
    }
    
    /// 模式匹配(支持 /product/:id/:type 这种格式)
    private func matchPattern(path: String, pattern: String) -> [String: String]? {
        let pathComponents = path.split(separator: "/")
        let patternComponents = pattern.split(separator: "/")
        
        guard pathComponents.count == patternComponents.count else { return nil }
        
        var params: [String: String] = [:]
        
        for (pathPart, patternPart) in zip(pathComponents, patternComponents) {
            if patternPart.hasPrefix(":") {
                // 参数占位符
                let paramName = String(patternPart.dropFirst())
                params[paramName] = String(pathPart)
            } else if patternPart == "*" {
                // 通配符
                continue
            } else if pathPart.lowercased() != patternPart.lowercased() {
                return nil
            }
        }
        
        return params
    }
    
    /// 执行拦截器链
    private func runInterceptors(
        url: URL,
        params: RouteParams,
        completion: @escaping (InterceptResult) -> Void
    ) {
        let applicableInterceptors = interceptors.filter { $0.shouldIntercept(url: url, params: params) }
        
        guard !applicableInterceptors.isEmpty else {
            completion(.continue)
            return
        }
        
        // 递归执行拦截器
        runNextInterceptor(
            interceptors: applicableInterceptors,
            index: 0,
            url: url,
            params: params,
            completion: completion
        )
    }
    
    private func runNextInterceptor(
        interceptors: [RouteInterceptor],
        index: Int,
        url: URL,
        params: RouteParams,
        completion: @escaping (InterceptResult) -> Void
    ) {
        guard index < interceptors.count else {
            completion(.continue)
            return
        }
        
        let interceptor = interceptors[index]
        interceptor.intercept(url: url, params: params) { [weak self] result in
            switch result {
            case .continue:
                // 继续下一个拦截器
                self?.runNextInterceptor(
                    interceptors: interceptors,
                    index: index + 1,
                    url: url,
                    params: params,
                    completion: completion
                )
            default:
                completion(result)
            }
        }
    }
    
    /// 执行路由
    private func performRouting(
        url: URL,
        params: RouteParams,
        from sourceVC: UIViewController?,
        options: RouteOptions
    ) {
        let result = matchRoute(url: url, params: params)
        
        DispatchQueue.main.async { [weak self] in
            switch result {
            case .viewController(let targetVC):
                self?.navigate(to: targetVC, from: sourceVC, options: options)
                
            case .handler(let action):
                action()
                options.completion?()
                
            case .redirect(let newURL):
                self?.open(url: newURL, from: sourceVC, options: options)
                
            case .failed(let error):
                self?.handleError(error, for: url)
                if let notFoundVC = self?.notFoundHandler?(url) {
                    self?.navigate(to: notFoundVC, from: sourceVC, options: options)
                }
            }
        }
    }
    
    /// 执行导航
    private func navigate(
        to targetVC: UIViewController,
        from sourceVC: UIViewController?,
        options: RouteOptions
    ) {
        let source = sourceVC ?? topViewController()
        
        guard let source = source else {
            print("❌ [Router] No source view controller")
            return
        }
        
        switch options.navigationType {
        case .push:
            if let nav = source.navigationController {
                nav.pushViewController(targetVC, animated: options.animated)
            } else {
                source.present(targetVC, animated: options.animated, 
                              completion: options.completion)
            }
            
        case .present:
            source.present(targetVC, animated: options.animated, 
                          completion: options.completion)
            
        case .presentFullScreen:
            targetVC.modalPresentationStyle = .fullScreen
            source.present(targetVC, animated: options.animated, 
                          completion: options.completion)
            
        case .replace:
            if let nav = source.navigationController {
                var viewControllers = nav.viewControllers
                viewControllers.removeLast()
                viewControllers.append(targetVC)
                nav.setViewControllers(viewControllers, animated: options.animated)
            }
            
        case .root:
            if let nav = source.navigationController {
                nav.setViewControllers([targetVC], animated: options.animated)
            } else if let window = source.view.window {
                window.rootViewController = targetVC
            }
            
        case .custom(let navigator):
            navigator(source, targetVC)
        }
    }
    
    /// 构建URL
    private func buildURL(_ path: String, params: RouteParams) -> URL? {
        var components = URLComponents()
        components.scheme = scheme
        components.path = path
        
        if !params.queryParams.isEmpty {
            components.queryItems = params.queryParams.map { 
                URLQueryItem(name: $0.key, value: $0.value) 
            }
        }
        
        return components.url
    }
    
    /// 获取顶层ViewController
    private func topViewController() -> UIViewController? {
        guard let window = UIApplication.shared.windows.first(where: { $0.isKeyWindow }),
              let rootVC = window.rootViewController else {
            return nil
        }
        return findTopViewController(from: rootVC)
    }
    
    private func findTopViewController(from vc: UIViewController) -> UIViewController {
        if let presented = vc.presentedViewController {
            return findTopViewController(from: presented)
        }
        if let nav = vc as? UINavigationController,
           let topVC = nav.topViewController {
            return findTopViewController(from: topVC)
        }
        if let tab = vc as? UITabBarController,
           let selectedVC = tab.selectedViewController {
            return findTopViewController(from: selectedVC)
        }
        return vc
    }
    
    /// 通知监听器
    private func notifyListeners(url: URL, params: RouteParams) {
        for listener in globalListeners {
            listener(url, params)
        }
    }
    
    /// 错误处理
    private func handleError(_ error: RouteError, for url: URL) {
        print("❌ [Router] Error: (error.localizedDescription)")
        errorHandler?(error, url)
    }
}

常用拦截器实现


// MARK: - 登录拦截器

public final class LoginInterceptor: RouteInterceptor {
    
    public var priority: Int { return 100 }
    
    /// 需要登录的路径
    private var protectedPatterns: [String] = [
        "/order",
        "/orders",
        "/cart",
        "/checkout",
        "/user/settings",
        "/user/address",
        "/wallet"
    ]
    
    /// 白名单路径(即使在受保护目录下也不需要登录)
    private var whitelistPatterns: [String] = [
        "/user/login",
        "/user/register",
        "/user/forgot-password"
    ]
    
    private let userService: UserServiceProtocol
    
    public init(userService: UserServiceProtocol) {
        self.userService = userService
    }
    
    public func shouldIntercept(url: URL, params: RouteParams) -> Bool {
        let path = url.path.lowercased()
        
        // 白名单放行
        if whitelistPatterns.contains(where: { path.hasPrefix($0) }) {
            return false
        }
        
        // 检查是否需要登录
        return protectedPatterns.contains(where: { path.hasPrefix($0) })
    }
    
    public func intercept(url: URL, params: RouteParams, 
                          completion: @escaping (InterceptResult) -> Void) {
        if userService.isLoggedIn() {
            completion(.continue)
        } else {
            // 保存目标URL
            savePendingURL(url)
            
            // 重定向到登录页
            let loginURL = URL(string: "myapp:///user/login?redirect=(url.absoluteString.urlEncoded)")!
            completion(.redirect(loginURL))
        }
    }
    
    /// 添加受保护路径
    public func addProtectedPattern(_ pattern: String) {
        protectedPatterns.append(pattern)
    }
    
    /// 添加白名单路径
    public func addWhitelistPattern(_ pattern: String) {
        whitelistPatterns.append(pattern)
    }
    
    private func savePendingURL(_ url: URL) {
        UserDefaults.standard.set(url.absoluteString, forKey: "pending_route_url")
    }
    
    /// 登录成功后调用,跳转到之前保存的URL
    public func resumePendingRoute() {
        guard let urlString = UserDefaults.standard.string(forKey: "pending_route_url"),
              let url = URL(string: urlString) else {
            return
        }
        
        UserDefaults.standard.removeObject(forKey: "pending_route_url")
        URLRouter.shared.open(url: url)
    }
}

extension String {
    var urlEncoded: String {
        return addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed) ?? self
    }
}

// MARK: - 降级拦截器

public final class DegradeInterceptor: RouteInterceptor {
    
    public var priority: Int { return 90 }
    
    /// 降级配置(可从服务器动态获取)
    private var degradeConfig: [String: DegradeAction] = [:]
    
    public enum DegradeAction {
        case block(message: String)              // 阻止访问
        case redirect(url: String)               // 重定向
        case webView(url: String)                // 降级到H5
        case toast(message: String)              // 提示后继续
    }
    
    public init() {}
    
    /// 更新降级配置
    public func updateConfig(_ config: [String: DegradeAction]) {
        self.degradeConfig = config
    }
    
    public func shouldIntercept(url: URL, params: RouteParams) -> Bool {
        let path = url.path.lowercased()
        return degradeConfig.keys.contains { path.hasPrefix($0) }
    }
    
    public func intercept(url: URL, params: RouteParams, 
                          completion: @escaping (InterceptResult) -> Void) {
        let path = url.path.lowercased()
        
        // 查找匹配的降级配置
        guard let (_, action) = degradeConfig.first(where: { path.hasPrefix($0.key) }) else {
            completion(.continue)
            return
        }
        
        switch action {
        case .block(let message):
            showToast(message)
            completion(.handled)
            
        case .redirect(let urlString):
            if let redirectURL = URL(string: urlString) {
                completion(.redirect(redirectURL))
            } else {
                completion(.continue)
            }
            
        case .webView(let webURLString):
            let h5URL = URL(string: "myapp:///webview?url=(webURLString.urlEncoded)")!
            completion(.redirect(h5URL))
            
        case .toast(let message):
            showToast(message)
            completion(.continue)
        }
    }
    
    private func showToast(_ message: String) {
        // 显示Toast提示
        print("📢 [Degrade] (message)")
    }
}

// MARK: - 统计埋点拦截器

public final class AnalyticsInterceptor: RouteInterceptor {
    
    public var priority: Int { return 50 }
    
    private let analyticsService: AnalyticsServiceProtocol
    
    public init(analyticsService: AnalyticsServiceProtocol) {
        self.analyticsService = analyticsService
    }
    
    public func shouldIntercept(url: URL, params: RouteParams) -> Bool {
        // 所有路由都需要埋点
        return true
    }
    
    public func intercept(url: URL, params: RouteParams, 
                          completion: @escaping (InterceptResult) -> Void) {
        // 异步埋点,不阻塞路由
        analyticsService.trackPageView(
            path: url.path,
            params: params.queryParams,
            source: params.queryParams["source"]
        )
        
        // 继续路由
        completion(.continue)
    }
}

// MARK: - AB测试拦截器

public final class ABTestInterceptor: RouteInterceptor {
    
    public var priority: Int { return 80 }
    
    /// AB测试配置
    private var experiments: [String: ABExperiment] = [:]
    
    public struct ABExperiment {
        let originalPath: String
        let variants: [String: String]  // variantName -> redirectPath
        let distribution: [String: Double]  // variantName -> percentage
    }
    
    public init() {}
    
    public func updateExperiments(_ experiments: [String: ABExperiment]) {
        self.experiments = experiments
    }
    
    public func shouldIntercept(url: URL, params: RouteParams) -> Bool {
        return experiments.keys.contains(url.path)
    }
    
    public func intercept(url: URL, params: RouteParams, 
                          completion: @escaping (InterceptResult) -> Void) {
        guard let experiment = experiments[url.path] else {
            completion(.continue)
            return
        }
        
        // 决定用户分组
        let variant = determineVariant(for: experiment)
        
        if let redirectPath = experiment.variants[variant],
           let redirectURL = URL(string: "myapp://(redirectPath)") {
            completion(.redirect(redirectURL))
        } else {
            completion(.continue)
        }
    }
    
    private func determineVariant(for experiment: ABExperiment) -> String {
        // 根据用户ID或设备ID决定分组,保证同一用户每次分组一致
        let random = Double.random(in: 0...1)
        var cumulative: Double = 0
        
        for (variant, percentage) in experiment.distribution {
            cumulative += percentage
            if random <= cumulative {
                return variant
            }
        }
        
        return "control"
    }
}

路由注册与使用示例


// MARK: - 模块路由注册

/// 商品模块路由注册
extension ProductModule {
    
    static func registerRoutes(router: URLRouter) {
        
        // 商品详情页
        router.register("/product/:id") { url, params in
            guard let productId = params.string("id") else { return nil }
            return ProductDetailViewController(productId: productId)
        }
        
        // 商品列表页
        router.register("/products") { url, params in
            let category = params.string("category")
            let sort = params.string("sort")
            return ProductListViewController(category: category, sortType: sort)
        }
        
        // 商品搜索页
        router.register("/search") { url, params in
            let keyword = params.string("keyword")
            return SearchViewController(keyword: keyword)
        }
        
        // 商品分类页
        router.register("/category/:id") { url, params in
            guard let categoryId = params.string("id") else { return nil }
            return CategoryViewController(categoryId: categoryId)
        }
        
        // 品牌页
        router.register("/brand/:id") { url, params in
            guard let brandId = params.string("id") else { return nil }
            return BrandViewController(brandId: brandId)
        }
    }
}

/// 订单模块路由注册
extension OrderModule {
    
    static func registerRoutes(router: URLRouter) {
        
        // 订单列表
        router.register("/orders") { url, params in
            let status = params.string("status")
            return OrderListViewController(statusFilter: status)
        }
        
        // 订单详情
        router.register("/order/:id") { url, params in
            guard let orderId = params.string("id") else { return nil }
            return OrderDetailViewController(orderId: orderId)
        }
        
        // 订单确认页
        router.register("/checkout") { url, params in
            // 从extra获取复杂对象
            let cartItems: [CartItem]? = params.get("cartItems")
            return CheckoutViewController(items: cartItems ?? [])
        }
        
        // 支付结果页
        router.register("/payment/result") { url, params in
            let orderId = params.string("orderId") ?? ""
            let success = params.bool("success")
            return PaymentResultViewController(orderId: orderId, success: success)
        }
    }
}

/// WebView路由注册
extension WebViewModule {
    
    static func registerRoutes(router: URLRouter) {
        
        // 通用WebView
        router.register("/webview") { url, params in
            guard let urlString = params.string("url"),
                  let webURL = URL(string: urlString) else {
                return nil
            }
            let title = params.string("title")
            return WebViewController(url: webURL, title: title)
        }
        
        // 帮助中心
        router.register("/help") { url, params in
            let webURL = URL(string: "https://help.yourcompany.com")!
            return WebViewController(url: webURL, title: "帮助中心")
        }
        
        // 隐私政策
        router.register("/privacy") { url, params in
            let webURL = URL(string: "https://yourcompany.com/privacy")!
            return WebViewController(url: webURL, title: "隐私政策")
        }
    }
}

// MARK: - 动作路由注册

extension ActionRoutes {
    
    static func registerRoutes(router: URLRouter) {
        
        // 分享动作
        router.registerAction("/action/share") { url, params in
            guard let title = params.string("title"),
                  let shareURL = params.string("url") else { return }
            
            ShareManager.share(title: title, url: shareURL)
        }
        
        // 拨打电话
        router.registerAction("/action/call") { url, params in
            guard let phone = params.string("phone"),
                  let phoneURL = URL(string: "tel://(phone)") else { return }
            
            UIApplication.shared.open(phoneURL)
        }
        
        // 复制到剪贴板
        router.registerAction("/action/copy") { url, params in
            guard let text = params.string("text") else { return }
            
            UIPasteboard.general.string = text
            Toast.show("已复制到剪贴板")
        }
        
        // 显示弹窗
        router.registerAction("/action/alert") { url, params in
            let title = params.string("title") ?? ""
            let message = params.string("message") ?? ""
            
            let alert = UIAlertController(title: title, message: message, preferredStyle: .alert)
            alert.addAction(UIAlertAction(title: "确定", style: .default))
            
            UIApplication.topViewController()?.present(alert, animated: true)
        }
    }
}

// MARK: - 使用示例

class SomeViewController: UIViewController {
    
    private let router = URLRouter.shared
    
    // 方式1: 直接使用URL字符串
    func openProductDetail() {
        router.open("/product/12345")
    }
    
    // 方式2: 带查询参数
    func openProductList() {
        router.open("/products?category=phone&sort=price_asc")
    }
    
    // 方式3: 带额外参数(传递复杂对象)
    func openCheckout(items: [CartItem]) {
        router.open("/checkout", extra: ["cartItems": items])
    }
    
    // 方式4: 指定导航方式
    func presentLogin() {
        router.open("/user/login", 
                   options: RouteOptions(navigationType: .presentFullScreen))
    }
    
    // 方式5: 带完成回调
    func openSetting() {
        router.open("/user/settings",
                   options: RouteOptions(navigationType: .push) { 
                       print("设置页已打开")
                   })
    }
    
    // 方式6: 获取ViewController后自定义处理
    func customNavigation() {
        if let vc = router.viewController(for: "/product/123") {
            // 自定义转场动画等
            let transition = CATransition()
            transition.type = .fade
            navigationController?.view.layer.add(transition, forKey: nil)
            navigationController?.pushViewController(vc, animated: false)
        }
    }
    
    // 方式7: 检查是否可以跳转
    func checkAndOpen() {
        let url = "/some/path"
        if router.canOpen(url) {
            router.open(url)
        } else {
            print("该路由不可用")
        }
    }
}

Target-Action 方案

CTMediator 风格实现


// MARK: - Mediator 核心实现

/// 组件中间件(CTMediator 风格)
public final class Mediator {
    
    public static let shared = Mediator()
    
    /// Target实例缓存
    private var targetCache: [String: NSObject] = [:]
    
    /// 缓存锁
    private let lock = NSLock()
    
    private init() {}
    
    // ═══════════════════════════════════════════════════════════════
    // MARK: - 核心调用方法
    // ═══════════════════════════════════════════════════════════════
    
    /// 远程调用(URL方式)
    @discardableResult
    public func performAction(url: URL) -> Any? {
        guard let host = url.host else { return nil }
        
        // URL格式:myapp://target/action?params
        let pathComponents = url.pathComponents.filter { $0 != "/" }
        guard let actionName = pathComponents.first else { return nil }
        
        // 解析参数
        var params: [String: Any] = [:]
        if let components = URLComponents(url: url, resolvingAgainstBaseURL: false),
           let queryItems = components.queryItems {
            for item in queryItems {
                params[item.name] = item.value ?? ""
            }
        }
        
        return performTarget(host, action: actionName, params: params, shouldCacheTarget: false)
    }
    
    /// 本地调用
    @discardableResult
    public func performTarget(
        _ targetName: String,
        action actionName: String,
        params: [String: Any]? = nil,
        shouldCacheTarget: Bool = false
    ) -> Any? {
        
        // 1. 构造 Target 类名 (Swift需要带模块名)
        let targetClassString = "Target_(targetName)"
        let swiftTargetClassString = "(Bundle.main.infoDictionary?["CFBundleName"] as? String ?? "").(targetClassString)"
        
        // 2. 获取或创建 Target 实例
        var target: NSObject?
        
        if shouldCacheTarget {
            lock.lock()
            target = targetCache[targetClassString]
            lock.unlock()
        }
        
        if target == nil {
            // 尝试 Swift 类名
            if let targetClass = NSClassFromString(swiftTargetClassString) as? NSObject.Type {
                target = targetClass.init()
            }
            // 尝试 OC 类名
            else if let targetClass = NSClassFromString(targetClassString) as? NSObject.Type {
                target = targetClass.init()
            }
        }
        
        guard let targetInstance = target else {
            print("❌ [Mediator] Target not found: (targetClassString)")
            return notFoundAction(targetName: targetName, actionName: actionName)
        }
        
        // 缓存Target
        if shouldCacheTarget {
            lock.lock()
            targetCache[targetClassString] = targetInstance
            lock.unlock()
        }
        
        // 3. 构造 Action 选择器
        let actionString = "Action_(actionName):"
        let selector = NSSelectorFromString(actionString)
        
        // 4. 检查是否响应该方法
        guard targetInstance.responds(to: selector) else {
            print("❌ [Mediator] Action not found: (actionString)")
            return notFoundAction(targetName: targetName, actionName: actionName)
        }
        
        // 5. 执行调用
        print("📞 [Mediator] Calling: (targetClassString).(actionString)")
        return targetInstance.perform(selector, with: params)?.takeUnretainedValue()
    }
    
    /// 清理缓存的Target
    public func clearCache() {
        lock.lock()
        targetCache.removeAll()
        lock.unlock()
    }
    
    /// 移除指定Target缓存
    public func removeTarget(_ targetName: String) {
        let key = "Target_(targetName)"
        lock.lock()
        targetCache.removeValue(forKey: key)
        lock.unlock()
    }
    
    // MARK: - Not Found Handler
    
    private func notFoundAction(targetName: String, actionName: String) -> UIViewController {
        let vc = UIViewController()
        vc.view.backgroundColor = .systemBackground
        
        let label = UILabel()
        label.text = "页面走丢了~
(targetName)/(actionName)"
        label.textAlignment = .center
        label.numberOfLines = 0
        label.textColor = .secondaryLabel
        label.translatesAutoresizingMaskIntoConstraints = false
        
        vc.view.addSubview(label)
        NSLayoutConstraint.activate([
            label.centerXAnchor.constraint(equalTo: vc.view.centerXAnchor),
            label.centerYAnchor.constraint(equalTo: vc.view.centerYAnchor),
            label.leadingAnchor.constraint(greaterThanOrEqualTo: vc.view.leadingAnchor, constant: 20)
        ])
        
        return vc
    }
}

Target 实现示例


// MARK: - 商品模块 Target

/// Target 命名规范:Target_模块名
/// Action 命名规范:Action_操作名:
@objc class Target_Product: NSObject {
    
    /// 获取商品详情页
    @objc func Action_DetailViewController(_ params: [String: Any]) -> UIViewController {
        guard let productId = params["productId"] as? String else {
            return createErrorViewController("商品ID不能为空")
        }
        
        let vc = ProductDetailViewController(productId: productId)
        
        // 可选参数
        if let source = params["source"] as? String {
            vc.source = source
        }
        
        return vc
    }
    
    /// 获取商品列表页
    @objc func Action_ListViewController(_ params: [String: Any]) -> UIViewController {
        let category = params["category"] as? String
        let sortType = params["sortType"] as? String
        
        return ProductListViewController(category: category, sortType: sortType)
    }
    
    /// 获取搜索页
    @objc func Action_SearchViewController(_ params: [String: Any]) -> UIViewController {
        let keyword = params["keyword"] as? String
        return SearchViewController(keyword: keyword)
    }
    
    /// 获取商品数据(非页面调用)
    @objc func Action_FetchProductInfo(_ params: [String: Any]) -> [String: Any]? {
        guard let productId = params["productId"] as? String else {
            return nil
        }
        
        // 同步获取商品信息(实际应该是从缓存获取)
        return ProductCache.shared.getProduct(id: productId)?.toDictionary()
    }
    
    /// 添加商品到购物车(动作调用)
    @objc func Action_AddToFavorite(_ params: [String: Any]) -> NSNumber {
        guard let productId = params["productId"] as? String else {
            return NSNumber(value: false)
        }
        
        let success = FavoriteManager.shared.add(productId: productId)
        return NSNumber(value: success)
    }
    
    // MARK: - Helper
    
    private func createErrorViewController(_ message: String) -> UIViewController {
        let vc = UIViewController()
        vc.view.backgroundColor = .systemBackground
        
        let label = UILabel()
        label.text = message
        label.textColor = .systemRed
        label.translatesAutoresizingMaskIntoConstraints = false
        
        vc.view.addSubview(label)
        NSLayoutConstraint.activate([
            label.centerXAnchor.constraint(equalTo: vc.view.centerXAnchor),
            label.centerYAnchor.constraint(equalTo: vc.view.centerYAnchor)
        ])
        
        return vc
    }
}

// MARK: - 购物车模块 Target

@objc class Target_Cart: NSObject {
    
    /// 获取购物车页面
    @objc func Action_ViewController(_ params: [String: Any]) -> UIViewController {
        return CartViewController()
    }
    
    /// 添加商品到购物车
    @objc func Action_AddItem(_ params: [String: Any]) -> NSNumber {
        guard let productId = params["productId"] as? String else {
            return NSNumber(value: false)
        }
        
        let quantity = (params["quantity"] as? Int) ?? 1
        
        do {
            try CartManager.shared.addItem(productId: productId, quantity: quantity)
            return NSNumber(value: true)
        } catch {
            return NSNumber(value: false)
        }
    }
    
    /// 获取购物车数量
    @objc func Action_GetItemCount(_ params: [String: Any]) -> NSNumber {
        let count = CartManager.shared.totalItemCount
        return NSNumber(value: count)
    }
    
    /// 获取购物车商品列表(返回可编码的字典数组)
    @objc func Action_GetItems(_ params: [String: Any]) -> [[String: Any]] {
        let items = CartManager.shared.items
        return items.map { $0.toDictionary() }
    }
}

// MARK: - 用户模块 Target

@objc class Target_User: NSObject {
    
    /// 获取登录页面
    @objc func Action_LoginViewController(_ params: [String: Any]) -> UIViewController {
        let loginVC = LoginViewController()
        
        // 设置登录成功回调
        if let callback = params["callback"] as? ((Bool) -> Void) {
            loginVC.onComplete = callback
        }
        
        let nav = UINavigationController(rootViewController: loginVC)
        nav.modalPresentationStyle = .fullScreen
        return nav
    }
    
    /// 获取用户中心页面
    @objc func Action_CenterViewController(_ params: [String: Any]) -> UIViewController {
        return UserCenterViewController()
    }
    
    /// 检查登录状态
    @objc func Action_IsLoggedIn(_ params: [String: Any]) -> NSNumber {
        return NSNumber(value: UserManager.shared.isLoggedIn)
    }
    
    /// 获取当前用户信息
    @objc func Action_CurrentUserInfo(_ params: [String: Any]) -> [String: Any]? {
        return UserManager.shared.currentUser?.toDictionary()
    }
    
    /// 登出
    @objc func Action_Logout(_ params: [String: Any]) -> NSNumber {
        UserManager.shared.logout()
        return NSNumber(value: true)
    }
}

// MARK: - 订单模块 Target

@objc class Target_Order: NSObject {
    
    /// 订单列表页
    @objc func Action_ListViewController(_ params: [String: Any]) -> UIViewController {
        let status = params["status"] as? String
        return OrderListViewController(statusFilter: status)
    }
    
    /// 订单详情页
    @objc func Action_DetailViewController(_ params: [String: Any]) -> UIViewController {
        guard let orderId = params["orderId"] as? String else {
            return createErrorViewController("订单ID不能为空")
        }
        return OrderDetailViewController(orderId: orderId)
    }
    
    /// 结算页
    @objc func Action_CheckoutViewController(_ params: [String: Any]) -> UIViewController {
        // 支持传入CartItem对象数组
        if let items = params["items"] as? [CartItem] {
            return CheckoutViewController(items: items)
        }
        
        // 或者传入商品ID和数量
        if let productId = params["productId"] as? String,
           let quantity = params["quantity"] as? Int {
            return CheckoutViewController(productId: productId, quantity: quantity)
        }
        
        return createErrorViewController("参数错误")
    }
    
    private func createErrorViewController(_ message: String) -> UIViewController {
        let vc = UIViewController()
        vc.title = "错误"
        let label = UILabel()
        label.text = message
        label.textColor = .systemRed
        vc.view.addSubview(label)
        return vc
    }
}

类型安全的 Mediator 扩展


// MARK: - 类型安全的扩展(推荐使用方式)

/// 商品模块扩展
public extension Mediator {
    
    /// 商品详情页
    func productDetailViewController(productId: String, source: String? = nil) -> UIViewController {
        var params: [String: Any] = ["productId": productId]
        if let source = source {
            params["source"] = source
        }
        return performTarget("Product", action: "DetailViewController", 
                            params: params) as? UIViewController 
            ?? UIViewController()
    }
    
    /// 商品列表页
    func productListViewController(category: String? = nil, 
                                    sortType: String? = nil) -> UIViewController {
        var params: [String: Any] = [:]
        if let category = category { params["category"] = category }
        if let sortType = sortType { params["sortType"] = sortType }
        return performTarget("Product", action: "ListViewController", 
                            params: params) as? UIViewController 
            ?? UIViewController()
    }
    
    /// 搜索页
    func searchViewController(keyword: String? = nil) -> UIViewController {
        let params: [String: Any] = keyword != nil ? ["keyword": keyword!] : [:]
        return performTarget("Product", action: "SearchViewController", 
                            params: params) as? UIViewController 
            ?? UIViewController()
    }
    
    /// 获取商品信息
    func fetchProductInfo(productId: String) -> [String: Any]? {
        return performTarget("Product", action: "FetchProductInfo", 
                            params: ["productId": productId]) as? [String: Any]
    }
    
    /// 添加收藏
    @discardableResult
    func addToFavorite(productId: String) -> Bool {
        let result = performTarget("Product", action: "AddToFavorite", 
                                   params: ["productId": productId])
        return (result as? NSNumber)?.boolValue ?? false
    }
}

/// 购物车模块扩展
public extension Mediator {
    
    /// 购物车页面
    func cartViewController() -> UIViewController {
        return performTarget("Cart", action: "ViewController") as? UIViewController 
            ?? UIViewController()
    }
    
    /// 添加商品到购物车
    @discardableResult
    func addToCart(productId: String, quantity: Int = 1) -> Bool {
        let result = performTarget("Cart", action: "AddItem", 
                                   params: ["productId": productId, "quantity": quantity])
        return (result as? NSNumber)?.boolValue ?? false
    }
    
    /// 获取购物车数量
    func cartItemCount() -> Int {
        let result = performTarget("Cart", action: "GetItemCount")
        return (result as? NSNumber)?.intValue ?? 0
    }
    
    /// 获取购物车列表
    func cartItems() -> [[String: Any]] {
        return performTarget("Cart", action: "GetItems") as? [[String: Any]] ?? []
    }
}

/// 用户模块扩展
public extension Mediator {
    
    /// 登录页面
    func loginViewController(completion: ((Bool) -> Void)? = nil) -> UIViewController {
        var params: [String: Any] = [:]
        if let completion = completion {
            params["callback"] = completion
        }
        return performTarget("User", action: "LoginViewController", 
                            params: params) as? UIViewController 
            ?? UIViewController()
    }
    
    /// 用户中心
    func userCenterViewController() -> UIViewController {
        return performTarget("User", action: "CenterViewController") as? UIViewController 
            ?? UIViewController()
    }
    
    /// 是否已登录
    var isLoggedIn: Bool {
        let result = performTarget("User", action: "IsLoggedIn")
        return (result as? NSNumber)?.boolValue ?? false
    }
    
    /// 当前用户信息
    func currentUserInfo() -> [String: Any]? {
        return performTarget("User", action: "CurrentUserInfo") as? [String: Any]
    }
    
    /// 登出
    func logout() {
        performTarget("User", action: "Logout")
    }
    
    /// 如果需要登录,弹出登录页
    func requireLogin(from viewController: UIViewController, 
                      completion: @escaping (Bool) -> Void) {
        if isLoggedIn {
            completion(true)
            return
        }
        
        let loginVC = loginViewController(completion: completion)
        viewController.present(loginVC, animated: true)
    }
}

/// 订单模块扩展
public extension Mediator {
    
    /// 订单列表页
    func orderListViewController(status: String? = nil) -> UIViewController {
        let params: [String: Any] = status != nil ? ["status": status!] : [:]
        return performTarget("Order", action: "ListViewController", 
                            params: params) as? UIViewController 
            ?? UIViewController()
    }
    
    /// 订单详情页
    func orderDetailViewController(orderId: String) -> UIViewController {
        return performTarget("Order", action: "DetailViewController", 
                            params: ["orderId": orderId]) as? UIViewController 
            ?? UIViewController()
    }
    
    /// 结算页(通过购物车商品)
    func checkoutViewController(items: [CartItem]) -> UIViewController {
        return performTarget("Order", action: "CheckoutViewController", 
                            params: ["items": items]) as? UIViewController 
            ?? UIViewController()
    }
    
    /// 结算页(直接购买)
    func checkoutViewController(productId: String, quantity: Int) -> UIViewController {
        return performTarget("Order", action: "CheckoutViewController", 
                            params: ["productId": productId, "quantity": quantity]) as? UIViewController 
            ?? UIViewController()
    }
}

// MARK: - 使用示例

class ProductDetailViewController: UIViewController {
    
    private let productId: String
    var source: String?
    
    init(productId: String) {
        self.productId = productId
        super.init(nibName: nil, bundle: nil)
    }
    
    required init?(coder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
    
    // 添加到购物车
    @objc private func addToCartTapped() {
        // 方式1:通过扩展方法(类型安全,推荐)
        let success = Mediator.shared.addToCart(productId: productId, quantity: 1)
        
        if success {
            showToast("添加成功")
            updateCartBadge()
        } else {
            showToast("添加失败")
        }
    }
    
    // 立即购买
    @objc private func buyNowTapped() {
        // 先检查登录状态
        Mediator.shared.requireLogin(from: self) { [weak self] success in
            guard success, let self = self else { return }
            
            // 跳转结算页
            let checkoutVC = Mediator.shared.checkoutViewController(
                productId: self.productId, 
                quantity: 1
            )
            self.navigationController?.pushViewController(checkoutVC, animated: true)
        }
    }
    
    // 查看购物车
    @objc private func viewCartTapped() {
        let cartVC = Mediator.shared.cartViewController()
        navigationController?.pushViewController(cartVC, animated: true)
    }
    
    private func updateCartBadge() {
        let count = Mediator.shared.cartItemCount()
        // 更新TabBar角标
        tabBarController?.tabBar.items?[2].badgeValue = count > 0 ? "(count)" : nil
    }
    
    private func showToast(_ message: String) {
        // 显示Toast
    }
}

Protocol-Class 方案

协议注册机制实现


// MARK: - Protocol-Class 路由实现

import Foundation

// ═══════════════════════════════════════════════════════════════════
// MARK: - 模块协议定义
// ═══════════════════════════════════════════════════════════════════

/// 模块协议基类
public protocol ModuleServiceProtocol: AnyObject {
    /// 模块初始化
    static func moduleDidLoad()
}

/// 商品模块协议
public protocol ProductModuleProtocol: ModuleServiceProtocol {
    /// 商品详情页
    func productDetailViewController(productId: String) -> UIViewController
    
    /// 商品列表页
    func productListViewController(category: String?) -> UIViewController
    
    /// 搜索页
    func searchViewController(keyword: String?) -> UIViewController
    
    /// 获取商品信息
    func fetchProduct(by id: String) async throws -> ProductInfo
    
    /// 批量获取商品
    func fetchProducts(ids: [String]) async throws -> [ProductInfo]
}

/// 购物车模块协议
public protocol CartModuleProtocol: ModuleServiceProtocol {
    /// 购物车页面
    func cartViewController() -> UIViewController
    
    /// 添加商品
    func addItem(productId: String, quantity: Int) async throws
    
    /// 移除商品
    func removeItem(itemId: String) async throws
    
    /// 获取购物车列表
    func fetchItems() async throws -> [CartItem]
    
    /// 购物车数量
    var itemCount: Int { get }
    
    /// 数量变化通知
    var itemCountPublisher: AnyPublisher<Int, Never> { get }
}

/// 用户模块协议
public protocol UserModuleProtocol: ModuleServiceProtocol {
    /// 登录页
    func loginViewController(completion: ((Bool) -> Void)?) -> UIViewController
    
    /// 用户中心页
    func userCenterViewController() -> UIViewController
    
    /// 是否已登录
    var isLoggedIn: Bool { get }
    
    /// 当前用户
    var currentUser: UserInfo? { get }
    
    /// 登录
    func login(phone: String, code: String) async throws -> UserInfo
    
    /// 登出
    func logout() async throws
    
    /// 登录状态发布者
    var loginStatePublisher: AnyPublisher<Bool, Never> { get }
}

/// 订单模块协议
public protocol OrderModuleProtocol: ModuleServiceProtocol {
    /// 订单列表页
    func orderListViewController(status: OrderStatus?) -> UIViewController
    
    /// 订单详情页
    func orderDetailViewController(orderId: String) -> UIViewController
    
    /// 结算页
    func checkoutViewController(items: [CartItem]) -> UIViewController
    
    /// 创建订单
    func createOrder(items: [CartItem], addressId: String) async throws -> OrderInfo
    
    /// 取消订单
    func cancelOrder(orderId: String) async throws
}

// ═══════════════════════════════════════════════════════════════════
// MARK: - 模块注册器
// ═══════════════════════════════════════════════════════════════════

/// 模块注册器(类似 BeeHive 的实现)
public final class ModuleManager {
    
    public static let shared = ModuleManager()
    
    /// 协议 -> 实现类 映射
    private var protocolClassMap: [String: AnyClass] = [:]
    
    /// 协议 -> 实例 映射(单例)
    private var protocolInstanceMap: [String: Any] = [:]
    
    /// 模块类列表
    private var moduleClasses: [ModuleServiceProtocol.Type] = []
    
    /// 是否已初始化
    private var isInitialized = false
    
    /// 线程锁
    private let lock = NSRecursiveLock()
    
    private init() {}
    
    // ═══════════════════════════════════════════════════════════════
    // MARK: - 注册
    // ═══════════════════════════════════════════════════════════════
    
    /// 注册协议与实现类的映射
    public func register<Protocol, Implementation: AnyObject>(
        protocol: Protocol.Type,
        implementation: Implementation.Type
    ) {
        lock.lock()
        defer { lock.unlock() }
        
        let protocolKey = String(describing: `protocol`)
        protocolClassMap[protocolKey] = implementation
        
        print("📝 [ModuleManager] Registered: (protocolKey) -> (implementation)")
    }
    
    /// 注册协议与实例的映射(单例模式)
    public func register<Protocol>(
        protocol: Protocol.Type,
        instance: Protocol
    ) {
        lock.lock()
        defer { lock.unlock() }
        
        let protocolKey = String(describing: `protocol`)
        protocolInstanceMap[protocolKey] = instance
        
        print("📝 [ModuleManager] Registered Instance: (protocolKey)")
    }
    
    /// 注册模块类
    public func registerModule(_ moduleClass: ModuleServiceProtocol.Type) {
        lock.lock()
        defer { lock.unlock() }
        
        moduleClasses.append(moduleClass)
        print("📦 [ModuleManager] Module Registered: (moduleClass)")
    }
    
    /// 批量注册模块
    public func registerModules(_ classes: [ModuleServiceProtocol.Type]) {
        for moduleClass in classes {
            registerModule(moduleClass)
        }
    }
    
    // ═══════════════════════════════════════════════════════════════
    // MARK: - 获取服务
    // ═══════════════════════════════════════════════════════════════
    
    /// 获取协议对应的服务实例
    public func service<Protocol>(for protocol: Protocol.Type) -> Protocol? {
        lock.lock()
        defer { lock.unlock() }
        
        let protocolKey = String(describing: `protocol`)
        
        // 1. 先从实例缓存获取
        if let instance = protocolInstanceMap[protocolKey] as? Protocol {
            return instance
        }
        
        // 2. 从类映射创建实例
        if let implClass = protocolClassMap[protocolKey] as? NSObject.Type {
            let instance = implClass.init()
            
            // 缓存实例
            protocolInstanceMap[protocolKey] = instance
            
            if let service = instance as? Protocol {
                return service
            }
        }
        
        print("⚠️ [ModuleManager] Service not found for: (protocolKey)")
        return nil
    }
    
    /// 获取服务(强制解包)
    public func serviceRequired<Protocol>(for protocol: Protocol.Type) -> Protocol {
        guard let service = service(for: `protocol`) else {
            fatalError("❌ [ModuleManager] Required service not found: (String(describing: `protocol`))")
        }
        return service
    }
    
    // ═══════════════════════════════════════════════════════════════
    // MARK: - 初始化
    // ═══════════════════════════════════════════════════════════════
    
    /// 初始化所有模块
    public func initializeAllModules() {
        lock.lock()
        defer { lock.unlock() }
        
        guard !isInitialized else { return }
        
        print("🚀 [ModuleManager] Initializing (moduleClasses.count) modules...")
        
        for moduleClass in moduleClasses {
            moduleClass.moduleDidLoad()
            print("✅ [ModuleManager] Initialized: (moduleClass)")
        }
        
        isInitialized = true
        print("🎉 [ModuleManager] All modules initialized!")
    }
    
    /// 检查服务是否已注册
    public func isRegistered<Protocol>(for protocol: Protocol.Type) -> Bool {
        let protocolKey = String(describing: `protocol`)
        return protocolInstanceMap[protocolKey] != nil || protocolClassMap[protocolKey] != nil
    }
}

// ═══════════════════════════════════════════════════════════════════
// MARK: - 属性包装器
// ═══════════════════════════════════════════════════════════════════

/// 服务自动注入
@propertyWrapper
public struct ModuleService<Service> {
    
    private var service: Service?
    
    public init() {}
    
    public var wrappedValue: Service {
        mutating get {
            if service == nil {
                service = ModuleManager.shared.service(for: Service.self)
            }
            guard let service = service else {
                fatalError("❌ Service (Service.self) not registered")
            }
            return service
        }
    }
}

/// 可选服务注入
@propertyWrapper
public struct OptionalModuleService<Service> {
    
    public var wrappedValue: Service?
    
    public init() {
        self.wrappedValue = ModuleManager.shared.service(for: Service.self)
    }
}

模块实现示例


// MARK: - 商品模块实现

/// 商品模块实现类
public final class ProductModuleImpl: NSObject, ProductModuleProtocol {
    
    private let networkService: NetworkServiceProtocol
    private let cacheService: CacheServiceProtocol
    
    public override init() {
        // 从ModuleManager获取依赖服务
        self.networkService = ModuleManager.shared.serviceRequired(for: NetworkServiceProtocol.self)
        self.cacheService = ModuleManager.shared.serviceRequired(for: CacheServiceProtocol.self)
        super.init()
    }
    
    // MARK: - ModuleServiceProtocol
    
    public static func moduleDidLoad() {
        print("📦 [ProductModule] Module loaded")
        
        // 注册自己
        ModuleManager.shared.register(
            protocol: ProductModuleProtocol.self,
            implementation: ProductModuleImpl.self
        )
        
        // 预加载数据等
        Task {
            await preloadData()
        }
    }
    
    private static func preloadData() async {
        // 预加载商品分类等
    }
    
    // MARK: - ProductModuleProtocol
    
    public func productDetailViewController(productId: String) -> UIViewController {
        return ProductDetailViewController(productId: productId)
    }
    
    public func productListViewController(category: String?) -> UIViewController {
        return ProductListViewController(category: category)
    }
    
    public func searchViewController(keyword: String?) -> UIViewController {
        return SearchViewController(keyword: keyword)
    }
    
    public func fetchProduct(by id: String) async throws -> ProductInfo {
        // 先检查缓存
        let cacheKey = "product_(id)"
        if let cached: ProductInfo = cacheService.get(forKey: cacheKey) {
            return cached
        }
        
        // 网络请求
        let product: ProductInfo = try await networkService.request(
            endpoint: .productDetail(id: id)
        )
        
        // 缓存
        cacheService.set(product, forKey: cacheKey)
        
        return product
    }
    
    public func fetchProducts(ids: [String]) async throws -> [ProductInfo] {
        return try await networkService.request(
            endpoint: .productBatch(ids: ids)
        )
    }
}

// MARK: - 用户模块实现

public final class UserModuleImpl: NSObject, UserModuleProtocol {
    
    private let loginStateSubject = CurrentValueSubject<Bool, Never>(false)
    private var _currentUser: UserInfo?
    
    public override init() {
        super.init()
        restoreLoginState()
    }
    
    // MARK: - ModuleServiceProtocol
    
    public static func moduleDidLoad() {
        print("📦 [UserModule] Module loaded")
        
        ModuleManager.shared.register(
            protocol: UserModuleProtocol.self,
            implementation: UserModuleImpl.self
        )
    }
    
    // MARK: - UserModuleProtocol
    
    public func loginViewController(completion: ((Bool) -> Void)?) -> UIViewController {
        let loginVC = LoginViewController()
        loginVC.onComplete = { [weak self] success in
            self?.loginStateSubject.send(success)
            completion?(success)
        }
        let nav = UINavigationController(rootViewController: loginVC)
        nav.modalPresentationStyle = .fullScreen
        return nav
    }
    
    public func userCenterViewController() -> UIViewController {
        return UserCenterViewController()
    }
    
    public var isLoggedIn: Bool {
        return _currentUser != nil
    }
    
    public var currentUser: UserInfo? {
        return _currentUser
    }
    
    public var loginStatePublisher: AnyPublisher<Bool, Never> {
        return loginStateSubject.eraseToAnyPublisher()
    }
    
    public func login(phone: String, code: String) async throws -> UserInfo {
        // 登录请求
        let user = try await performLogin(phone: phone, code: code)
        
        _currentUser = user
        saveLoginState(user: user)
        loginStateSubject.send(true)
        
        // 发送登录成功通知
        NotificationCenter.default.post(name: .userDidLogin, object: user)
        
        return user
    }
    
    public func logout() async throws {
        // 登出请求
        try await performLogout()
        
        _currentUser = nil
        clearLoginState()
        loginStateSubject.send(false)
        
        // 发送登出通知
        NotificationCenter.default.post(name: .userDidLogout, object: nil)
    }
    
    // MARK: - Private
    
    private func performLogin(phone: String, code: String) async throws -> UserInfo {
        // 实际登录请求
        let networkService = ModuleManager.shared.serviceRequired(for: NetworkServiceProtocol.self)
        return try await networkService.request(
            endpoint: .login(phone: phone, code: code)
        )
    }
    
    private func performLogout() async throws {
        let networkService = ModuleManager.shared.serviceRequired(for: NetworkServiceProtocol.self)
        try await networkService.request(endpoint: .logout)
    }
    
    private func restoreLoginState() {
        if let data = KeychainHelper.load(forKey: "current_user"),
           let user = try? JSONDecoder().decode(UserInfo.self, from: data) {
            _currentUser = user
            loginStateSubject.send(true)
        }
    }
    
    private func saveLoginState(user: UserInfo) {
        if let data = try? JSONEncoder().encode(user) {
            KeychainHelper.save(data, forKey: "current_user")
        }
    }
    
    private func clearLoginState() {
        KeychainHelper.delete(forKey: "current_user")
    }
}

// 通知名称扩展
extension Notification.Name {
    static let userDidLogin = Notification.Name("userDidLogin")
    static let userDidLogout = Notification.Name("userDidLogout")
}

// MARK: - 购物车模块实现

public final class CartModuleImpl: NSObject, CartModuleProtocol {
    
    private var items: [CartItem] = []
    private let itemCountSubject = CurrentValueSubject<Int, Never>(0)
    private var cancellables = Set<AnyCancellable>()
    
    public override init() {
        super.init()
        setupObservers()
        loadLocalCart()
    }
    
    public static func moduleDidLoad() {
        print("📦 [CartModule] Module loaded")
        
        ModuleManager.shared.register(
            protocol: CartModuleProtocol.self,
            implementation: CartModuleImpl.self
        )
    }
    
    // MARK: - CartModuleProtocol
    
    public func cartViewController() -> UIViewController {
        return CartViewController()
    }
    
    public func addItem(productId: String, quantity: Int) async throws {
        let networkService = ModuleManager.shared.serviceRequired(for: NetworkServiceProtocol.self)
        
        try await networkService.request(
            endpoint: .addToCart(productId: productId, quantity: quantity)
        )
        
        // 刷新购物车
        _ = try await fetchItems()
    }
    
    public func removeItem(itemId: String) async throws {
        let networkService = ModuleManager.shared.serviceRequired(for: NetworkServiceProtocol.self)
        
        try await networkService.request(endpoint: .removeFromCart(itemId: itemId))
        
        // 刷新购物车
        _ = try await fetchItems()
    }
    
    public func fetchItems() async throws -> [CartItem] {
        let networkService = ModuleManager.shared.serviceRequired(for: NetworkServiceProtocol.self)
        
        items = try await networkService.request(endpoint: .cartList)
        
        let count = items.reduce(0) { $0 + $1.quantity }
        itemCountSubject.send(count)
        
        return items
    }
    
    public var itemCount: Int {
        return itemCountSubject.value
    }
    
    public var itemCountPublisher: AnyPublisher<Int, Never> {
        return itemCountSubject.eraseToAnyPublisher()
    }
    
    // MARK: - Private
    
    private func setupObservers() {
        // 监听登录状态,登出时清空购物车
        NotificationCenter.default.publisher(for: .userDidLogout)
            .sink { [weak self] _ in
                self?.items = []
                self?.itemCountSubject.send(0)
            }
            .store(in: &cancellables)
        
        // 登录后刷新购物车
        NotificationCenter.default.publisher(for: .userDidLogin)
            .sink { [weak self] _ in
                Task {
                    try? await self?.fetchItems()
                }
            }
            .store(in: &cancellables)
    }
    
    private func loadLocalCart() {
        // 从本地加载购物车缓存
        let count = UserDefaults.standard.integer(forKey: "cart_item_count")
        itemCountSubject.send(count)
    }
}

Protocol-Class 使用示例


// MARK: - 模块初始化(AppDelegate 中)

class AppDelegate: UIResponder, UIApplicationDelegate {
    
    func application(
        _ application: UIApplication,
        didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
    ) -> Bool {
        
        // 1. 注册基础服务
        registerCoreServices()
        
        // 2. 注册所有业务模块
        registerBusinessModules()
        
        // 3. 初始化所有模块
        ModuleManager.shared.initializeAllModules()
        
        return true
    }
    
    private func registerCoreServices() {
        let manager = ModuleManager.shared
        
        // 网络服务
        manager.register(protocol: NetworkServiceProtocol.self, 
                        instance: NetworkServiceImpl())
        
        // 缓存服务
        manager.register(protocol: CacheServiceProtocol.self, 
                        instance: CacheServiceImpl())
        
        // 数据库服务
        manager.register(protocol: DatabaseServiceProtocol.self, 
                        instance: DatabaseServiceImpl())
    }
    
    private func registerBusinessModules() {
        ModuleManager.shared.registerModules([
            UserModuleImpl.self,      // 用户模块优先
            ProductModuleImpl.self,
            CartModuleImpl.self,
            OrderModuleImpl.self,
        ])
    }
}

// MARK: - 业务代码中使用

class HomeViewController: UIViewController {
    
    // 方式1:属性包装器注入
    @ModuleService private var productModule: ProductModuleProtocol
    @ModuleService private var cartModule: CartModuleProtocol
    @ModuleService private var userModule: UserModuleProtocol
    
    private var cancellables = Set<AnyCancellable>()
    
    override func viewDidLoad() {
        super.viewDidLoad()
        setupBindings()
        loadData()
    }
    
    private func setupBindings() {
        // 监听购物车数量变化
        cartModule.itemCountPublisher
            .receive(on: DispatchQueue.main)
            .sink { [weak self] count in
                self?.updateCartBadge(count)
            }
            .store(in: &cancellables)
        
        // 监听登录状态
        userModule.loginStatePublisher
            .receive(on: DispatchQueue.main)
            .sink { [weak self] isLoggedIn in
                self?.updateLoginStatus(isLoggedIn)
            }
            .store(in: &cancellables)
    }
    
    private func loadData() {
        Task {
            do {
                let products = try await productModule.fetchProducts(ids: ["1", "2", "3"])
                updateUI(with: products)
            } catch {
                showError(error)
            }
        }
    }
    
    // 跳转商品详情
    func showProductDetail(productId: String) {
        let vc = productModule.productDetailViewController(productId: productId)
        navigationController?.pushViewController(vc, animated: true)
    }
    
    // 添加到购物车
    func addToCart(productId: String) {
        // 检查登录
        guard userModule.isLoggedIn else {
            let loginVC = userModule.loginViewController { [weak self] success in
                if success {
                    self?.addToCart(productId: productId)
                }
            }
            present(loginVC, animated: true)
            return
        }
        
        Task {
            do {
                try await cartModule.addItem(productId: productId, quantity: 1)
                showToast("添加成功")
            } catch {
                showError(error)
            }
        }
    }
    
    // 查看购物车
    func showCart() {
        let vc = cartModule.cartViewController()
        navigationController?.pushViewController(vc, animated: true)
    }
    
    private func updateCartBadge(_ count: Int) {
        tabBarItem.badgeValue = count > 0 ? "(count)" : nil
    }
    
    private func updateLoginStatus(_ isLoggedIn: Bool) {
        // 更新UI
    }
    
    private func updateUI(with products: [ProductInfo]) {
        // 更新商品列表
    }
    
    private func showError(_ error: Error) {
        // 显示错误
    }
    
    private func showToast(_ message: String) {
        // 显示提示
    }
}

// MARK: - 方式2:直接通过 ModuleManager 获取

class SomeOtherViewController: UIViewController {
    
    func doSomething() {
        // 获取服务
        guard let productModule = ModuleManager.shared.service(for: ProductModuleProtocol.self) else {
            print("商品模块未注册")
            return
        }
        
        // 使用服务
        let vc = productModule.productDetailViewController(productId: "123")
        navigationController?.pushViewController(vc, animated: true)
    }
}

混合路由架构

统一路由门面


// MARK: - 统一路由门面(混合 URLRouter + Mediator + Protocol)

/// 统一路由器 - 整合多种路由方案
public final class UnifiedRouter {
    
    public static let shared = UnifiedRouter()
    
    private let urlRouter = URLRouter.shared
    private let mediator = Mediator.shared
    private let moduleManager = ModuleManager.shared
    
    private init() {}
    
    // ═══════════════════════════════════════════════════════════════
    // MARK: - URL 路由(页面跳转 + DeepLink)
    // ═══════════════════════════════════════════════════════════════
    
    /// 通过 URL 导航
    public func navigate(
        to urlString: String,
        from sourceVC: UIViewController? = nil,
        options: RouteOptions = .default,
        extra: [String: Any]? = nil
    ) {
        urlRouter.open(urlString, from: sourceVC, options: options, extra: extra)
    }
    
    /// 通过 URL 对象导航
    public func navigate(
        to url: URL,
        from sourceVC: UIViewController? = nil,
        options: RouteOptions = .default
    ) {
        urlRouter.open(url: url, from: sourceVC, options: options)
    }
    
    /// 检查 URL 是否可以处理
    public func canHandle(_ urlString: String) -> Bool {
        return urlRouter.canOpen(urlString)
    }
    
    // ═══════════════════════════════════════════════════════════════
    // MARK: - Mediator 调用(组件间服务调用)
    // ═══════════════════════════════════════════════════════════════
    
    /// Target-Action 调用
    @discardableResult
    public func perform(
        target: String,
        action: String,
        params: [String: Any]? = nil
    ) -> Any? {
        return mediator.performTarget(target, action: action, params: params)
    }
    
    // ═══════════════════════════════════════════════════════════════
    // MARK: - Protocol 服务获取
    // ═══════════════════════════════════════════════════════════════
    
    /// 获取模块服务
    public func service<T>(for protocol: T.Type) -> T? {
        return moduleManager.service(for: `protocol`)
    }
    
    /// 获取必需的模块服务
    public func serviceRequired<T>(for protocol: T.Type) -> T {
        return moduleManager.serviceRequired(for: `protocol`)
    }
    
    // ═══════════════════════════════════════════════════════════════
    // MARK: - 便捷方法
    // ═══════════════════════════════════════════════════════════════
    
    /// 返回上一页
    public func goBack(from viewController: UIViewController, animated: Bool = true) {
        if let nav = viewController.navigationController, nav.viewControllers.count > 1 {
            nav.popViewController(animated: animated)
        } else {
            viewController.dismiss(animated: animated)
        }
    }
    
    /// 返回根页面
    public func popToRoot(from viewController: UIViewController, animated: Bool = true) {
        viewController.navigationController?.popToRootViewController(animated: animated)
    }
    
    /// 关闭所有模态页面
    public func dismissAll(from viewController: UIViewController, 
                           animated: Bool = true,
                           completion: (() -> Void)? = nil) {
        var current = viewController
        while let presenting = current.presentingViewController {
            current = presenting
        }
        current.dismiss(animated: animated, completion: completion)
    }
}

// MARK: - 统一路由扩展 - 商品模块

public extension UnifiedRouter {
    
    // URL方式
    func openProductDetail(productId: String, from vc: UIViewController? = nil) {
        navigate(to: "/product/(productId)", from: vc)
    }
    
    func openProductList(category: String? = nil, from vc: UIViewController? = nil) {
        var url = "/products"
        if let category = category {
            url += "?category=(category)"
        }
        navigate(to: url, from: vc)
    }
    
    // Mediator方式(获取VC后自定义处理)
    func productDetailVC(productId: String) -> UIViewController {
        return mediator.productDetailViewController(productId: productId)
    }
    
    // Protocol方式(获取服务能力)
    var productService: ProductModuleProtocol {
        return serviceRequired(for: ProductModuleProtocol.self)
    }
}

// MARK: - 统一路由扩展 - 购物车模块

public extension UnifiedRouter {
    
    func openCart(from vc: UIViewController? = nil) {
        navigate(to: "/cart", from: vc)
    }
    
    @discardableResult
    func addToCart(productId: String, quantity: Int = 1) async throws -> Bool {
        let cartService = serviceRequired(for: CartModuleProtocol.self)
        try await cartService.addItem(productId: productId, quantity: quantity)
        return true
    }
    
    var cartItemCount: Int {
        return service(for: CartModuleProtocol.self)?.itemCount ?? 0
    }
}

// MARK: - 统一路由扩展 - 用户模块

public extension UnifiedRouter {
    
    var isLoggedIn: Bool {
        return service(for: UserModuleProtocol.self)?.isLoggedIn ?? false
    }
    
    var currentUser: UserInfo? {
        return service(for: UserModuleProtocol.self)?.currentUser
    }
    
    /// 需要登录时调用
    func requireLogin(
        from vc: UIViewController,
        completion: @escaping (Bool) -> Void
    ) {
        guard !isLoggedIn else {
            completion(true)
            return
        }
        
        guard let userService = service(for: UserModuleProtocol.self) else {
            completion(false)
            return
        }
        
        let loginVC = userService.loginViewController(completion: completion)
        vc.present(loginVC, animated: true)
    }
    
    func logout() async throws {
        try await serviceRequired(for: UserModuleProtocol.self).logout()
    }
}

路由配置表(动态化支持)


// MARK: - 路由配置(支持动态下发)

/// 路由配置模型
public struct RouteConfig: Codable {
    /// 路由路径
    let path: String
    
    /// 处理方式
    let handlerType: HandlerType
    
    /// 目标信息
    let target: TargetInfo?
    
    /// 是否需要登录
    let requireLogin: Bool
    
    /// 是否降级到H5
    let degradeToH5: Bool
    
    /// H5降级地址
    let h5URL: String?
    
    /// 最低支持版本
    let minVersion: String?
    
    /// 是否启用
    let enabled: Bool
    
    enum HandlerType: String, Codable {
        case native      // 原生页面
        case webView     // H5页面
        case action      // 执行动作
        case redirect    // 重定向
    }
    
    struct TargetInfo: Codable {
        let targetName: String
        let actionName: String
        let defaultParams: [String: String]?
    }
}

/// 路由配置管理器
public final class RouteConfigManager {
    
    public static let shared = RouteConfigManager()
    
    /// 本地配置
    private var localConfigs: [String: RouteConfig] = [:]
    
    /// 远程配置
    private var remoteConfigs: [String: RouteConfig] = [:]
    
    /// 当前App版本
    private let appVersion: String
    
    private init() {
        appVersion = Bundle.main.infoDictionary?["CFBundleShortVersionString"] as? String ?? "1.0.0"
        loadLocalConfigs()
    }
    
    /// 加载本地配置
    private func loadLocalConfigs() {
        guard let url = Bundle.main.url(forResource: "route_config", withExtension: "json"),
              let data = try? Data(contentsOf: url),
              let configs = try? JSONDecoder().decode([RouteConfig].self, from: data) else {
            return
        }
        
        for config in configs {
            localConfigs[config.path] = config
        }
    }
    
    /// 更新远程配置
    public func updateRemoteConfigs(_ configs: [RouteConfig]) {
        for config in configs {
            remoteConfigs[config.path] = config
        }
    }
    
    /// 获取路由配置
    public func config(for path: String) -> RouteConfig? {
        // 远程配置优先
        if let remote = remoteConfigs[path] {
            return validateConfig(remote) ? remote : nil
        }
        
        if let local = localConfigs[path] {
            return validateConfig(local) ? local : nil
        }
        
        return nil
    }
    
    /// 验证配置有效性
    private func validateConfig(_ config: RouteConfig) -> Bool {
        // 检查是否启用
        guard config.enabled else { return false }
        
        // 检查版本要求
        if let minVersion = config.minVersion {
            if appVersion.compare(minVersion, options: .numeric) == .orderedAscending {
                return false
            }
        }
        
        return true
    }
}

// MARK: - 配置驱动的路由拦截器

public final class ConfigDrivenInterceptor: RouteInterceptor {
    
    public var priority: Int { return 200 }
    
    public func shouldIntercept(url: URL, params: RouteParams) -> Bool {
        // 检查是否有对应配置
        return RouteConfigManager.shared.config(for: url.path) != nil
    }
    
    public func intercept(
        url: URL,
        params: RouteParams,
        completion: @escaping (InterceptResult) -> Void
    ) {
        guard let config = RouteConfigManager.shared.config(for: url.path) else {
            completion(.continue)
            return
        }
        
        // 检查登录要求
        if config.requireLogin && !UnifiedRouter.shared.isLoggedIn {
            let loginURL = URL(string: "myapp:///login?redirect=(url.absoluteString.urlEncoded)")!
            completion(.redirect(loginURL))
            return
        }
        
        // 检查是否降级
        if config.degradeToH5, let h5URL = config.h5URL {
            let webViewURL = URL(string: "myapp:///webview?url=(h5URL.urlEncoded)")!
            completion(.redirect(webViewURL))
            return
        }
        
        // 处理重定向
        if config.handlerType == .redirect, let target = config.target {
            let redirectURL = URL(string: "myapp:///(target.targetName)/(target.actionName)")!
            completion(.redirect(redirectURL))
            return
        }
        
        completion(.continue)
    }
}

使用示例


// MARK: - 最终使用示例

class ProductViewController: UIViewController {
    
    // 统一使用 UnifiedRouter
    private let router = UnifiedRouter.shared
    
    // MARK: - 页面跳转(URL方式)
    
    func showProductDetail(productId: String) {
        // 最简单的方式
        router.navigate(to: "/product/(productId)")
    }
    
    func showCart() {
        // 使用便捷方法
        router.openCart(from: self)
    }
    
    func showOrders() {
        // 指定导航方式
        router.navigate(
            to: "/orders",
            from: self,
            options: RouteOptions(navigationType: .push)
        )
    }
    
    // MARK: - 服务调用(Protocol方式)
    
    func loadProducts() async {
        do {
            let products = try await router.productService.fetchProducts(ids: ["1", "2", "3"])
            updateUI(products)
        } catch {
            showError(error)
        }
    }
    
    func addToCart(productId: String) async {
        // 检查登录
        router.requireLogin(from: self) { [weak self] success in
            guard success else { return }
            
            Task {
                do {
                    try await self?.router.addToCart(productId: productId)
                    self?.showToast("添加成功")
                } catch {
                    self?.showError(error)
                }
            }
        }
    }
    
    // MARK: - 获取ViewController自定义处理(Mediator方式)
    
    func showProductWithCustomAnimation(productId: String) {
        let vc = router.productDetailVC(productId: productId)
        
        // 自定义转场
        let transition = CATransition()
        transition.type = .fade
        transition.duration = 0.3
        navigationController?.view.layer.add(transition, forKey: nil)
        navigationController?.pushViewController(vc, animated: false)
    }
    
    // MARK: - 私有方法
    
    private func updateUI(_ products: [ProductInfo]) {}
    private func showError(_ error: Error) {}
    private func showToast(_ message: String) {}
}

高级特性实现

路由动画配置


// MARK: - 路由转场动画

/// 转场动画类型
public enum RouteTransition {
    case none
    case push
    case present
    case fade
    case slideFromRight
    case slideFromBottom
    case zoom
    case custom(UIViewControllerAnimatedTransitioning)
}

/// 转场动画管理器
public final class RouteTransitionManager: NSObject {
    
    public static let shared = RouteTransitionManager()
    
    private var transitions: [String: RouteTransition] = [:]
    
    /// 注册路由转场动画
    public func register(path: String, transition: RouteTransition) {
        transitions[path] = transition
    }
    
    /// 获取路由对应的转场动画
    public func transition(for path: String) -> RouteTransition {
        return transitions[path] ?? .push
    }
}

/// 自定义导航控制器(支持路由动画)
public class RouteNavigationController: UINavigationController {
    
    private var currentTransition: RouteTransition = .push
    
    public func pushViewController(
        _ viewController: UIViewController,
        transition: RouteTransition,
        animated: Bool
    ) {
        self.currentTransition = transition
        
        switch transition {
        case .none:
            pushViewController(viewController, animated: false)
            
        case .push:
            pushViewController(viewController, animated: animated)
            
        case .fade:
            let fadeTransition = CATransition()
            fadeTransition.type = .fade
            fadeTransition.duration = 0.3
            view.layer.add(fadeTransition, forKey: nil)
            pushViewController(viewController, animated: false)
            
        case .slideFromRight:
            let slideTransition = CATransition()
            slideTransition.type = .push
            slideTransition.subtype = .fromRight
            slideTransition.duration = 0.3
            view.layer.add(slideTransition, forKey: nil)
            pushViewController(viewController, animated: false)
            
        case .slideFromBottom:
            let slideTransition = CATransition()
            slideTransition.type = .push
            slideTransition.subtype = .fromTop
            slideTransition.duration = 0.3
            view.layer.add(slideTransition, forKey: nil)
            pushViewController(viewController, animated: false)
            
        case .zoom:
            delegate = self
            pushViewController(viewController, animated: animated)
            
        case .present:
            let nav = UINavigationController(rootViewController: viewController)
            present(nav, animated: animated)
            
        case .custom(let animator):
            delegate = self
            pushViewController(viewController, animated: animated)
        }
    }
}

extension RouteNavigationController: UINavigationControllerDelegate {
    
    public func navigationController(
        _ navigationController: UINavigationController,
        animationControllerFor operation: UINavigationController.Operation,
        from fromVC: UIViewController,
        to toVC: UIViewController
    ) -> UIViewControllerAnimatedTransitioning? {
        
        switch currentTransition {
        case .zoom:
            return ZoomTransitionAnimator(operation: operation)
        case .custom(let animator):
            return animator
        default:
            return nil
        }
    }
}

/// 缩放转场动画
class ZoomTransitionAnimator: NSObject, UIViewControllerAnimatedTransitioning {
    
    let operation: UINavigationController.Operation
    
    init(operation: UINavigationController.Operation) {
        self.operation = operation
        super.init()
    }
    
    func transitionDuration(
        using transitionContext: UIViewControllerContextTransitioning?
    ) -> TimeInterval {
        return 0.3
    }
    
    func animateTransition(
        using transitionContext: UIViewControllerContextTransitioning
    ) {
        let containerView = transitionContext.containerView
        
        guard let fromView = transitionContext.view(forKey: .from),
              let toView = transitionContext.view(forKey: .to) else {
            transitionContext.completeTransition(false)
            return
        }
        
        let duration = transitionDuration(using: transitionContext)
        
        if operation == .push {
            // Push: 新页面从小变大
            toView.transform = CGAffineTransform(scaleX: 0.8, y: 0.8)
            toView.alpha = 0
            containerView.addSubview(toView)
            
            UIView.animate(withDuration: duration, animations: {
                fromView.transform = CGAffineTransform(scaleX: 1.2, y: 1.2)
                fromView.alpha = 0
                toView.transform = .identity
                toView.alpha = 1
            }) { _ in
                fromView.transform = .identity
                fromView.alpha = 1
                transitionContext.completeTransition(!transitionContext.transitionWasCancelled)
            }
        } else {
            // Pop: 当前页面变小消失
            containerView.insertSubview(toView, belowSubview: fromView)
            toView.transform = CGAffineTransform(scaleX: 1.2, y: 1.2)
            toView.alpha = 0
            
            UIView.animate(withDuration: duration, animations: {
                fromView.transform = CGAffineTransform(scaleX: 0.8, y: 0.8)
                fromView.alpha = 0
                toView.transform = .identity
                toView.alpha = 1
            }) { _ in
                transitionContext.completeTransition(!transitionContext.transitionWasCancelled)
            }
        }
    }
}

路由栈管理


// MARK: - 路由栈管理

/// 路由栈管理器
public final class RouteStackManager {
    
    public static let shared = RouteStackManager()
    
    /// 路由历史记录
    private var routeHistory: [RouteRecord] = []
    
    /// 最大历史记录数
    private let maxHistoryCount = 50
    
    private init() {}
    
    /// 路由记录
    public struct RouteRecord {
        let url: URL
        let timestamp: Date
        let params: RouteParams
        let source: String?
    }
    
    /// 记录路由
    public func record(url: URL, params: RouteParams, source: String? = nil) {
        let record = RouteRecord(
            url: url,
            timestamp: Date(),
            params: params,
            source: source
        )
        
        routeHistory.append(record)
        
        // 限制历史记录数量
        if routeHistory.count > maxHistoryCount {
            routeHistory.removeFirst()
        }
    }
    
    /// 获取上一个路由
    public var previousRoute: RouteRecord? {
        guard routeHistory.count >= 2 else { return nil }
        return routeHistory[routeHistory.count - 2]
    }
    
    /// 获取路由历史
    public var history: [RouteRecord] {
        return routeHistory
    }
    
    /// 清空历史
    public func clearHistory() {
        routeHistory.removeAll()
    }
    
    /// 返回到指定路由
    public func popTo(path: String, from viewController: UIViewController) -> Bool {
        guard let nav = viewController.navigationController else { return false }
        
        // 在导航栈中查找目标
        for vc in nav.viewControllers.reversed() {
            if let routePath = vc.routePath, routePath == path {
                nav.popToViewController(vc, animated: true)
                return true
            }
        }
        
        return false
    }
    
    /// 移除指定路由之后的所有页面
    public func removeAfter(path: String, in viewController: UIViewController) {
        guard let nav = viewController.navigationController else { return }
        
        var targetIndex: Int?
        for (index, vc) in nav.viewControllers.enumerated() {
            if let routePath = vc.routePath, routePath == path {
                targetIndex = index
                break
            }
        }
        
        if let index = targetIndex, index < nav.viewControllers.count - 1 {
            nav.viewControllers = Array(nav.viewControllers.prefix(index + 1))
        }
    }
}

// 给 UIViewController 添加路由路径属性
private var routePathKey: UInt8 = 0

extension UIViewController {
    
    /// 当前页面对应的路由路径
    public var routePath: String? {
        get {
            return objc_getAssociatedObject(self, &routePathKey) as? String
        }
        set {
            objc_setAssociatedObject(self, &routePathKey, newValue, .OBJC_ASSOCIATION_RETAIN_NONATOMIC)
        }
    }
}

路由调试工具


// MARK: - 路由调试工具

#if DEBUG

/// 路由调试面板
public final class RouteDebugger {
    
    public static let shared = RouteDebugger()
    
    /// 是否启用调试
    public var isEnabled = true
    
    /// 路由日志
    private var logs: [RouteLog] = []
    
    private init() {}
    
    struct RouteLog {
        let timestamp: Date
        let url: String
        let params: [String: Any]
        let result: String
        let duration: TimeInterval
    }
    
    /// 记录路由日志
    public func log(
        url: String,
        params: [String: Any],
        result: String,
        duration: TimeInterval
    ) {
        guard isEnabled else { return }
        
        let log = RouteLog(
            timestamp: Date(),
            url: url,
            params: params,
            result: result,
            duration: duration
        )
        
        logs.append(log)
        
        print("""
        ════════════════════════════════════════
        🛣️ [Route Debug]
        URL: (url)
        Params: (params)
        Result: (result)
        Duration: (String(format: "%.3f", duration))s
        ════════════════════════════════════════
        """)
    }
    
    /// 显示调试面板
    public func showDebugPanel(from viewController: UIViewController) {
        let debugVC = RouteDebugViewController(logs: logs)
        let nav = UINavigationController(rootViewController: debugVC)
        viewController.present(nav, animated: true)
    }
    
    /// 导出日志
    public func exportLogs() -> String {
        var output = "Route Debug Logs

"
        
        let dateFormatter = DateFormatter()
        dateFormatter.dateFormat = "yyyy-MM-dd HH:mm:ss.SSS"
        
        for log in logs {
            output += """
            [(dateFormatter.string(from: log.timestamp))]
            URL: (log.url)
            Params: (log.params)
            Result: (log.result)
            Duration: (log.duration)s
            
            """
        }
        
        return output
    }
    
    /// 清空日志
    public func clearLogs() {
        logs.removeAll()
    }
}

/// 路由调试ViewController
class RouteDebugViewController: UITableViewController {
    
    private let logs: [RouteDebugger.RouteLog]
    
    init(logs: [RouteDebugger.RouteLog]) {
        self.logs = logs.reversed()
        super.init(style: .plain)
    }
    
    required init?(coder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        title = "Route Debug"
        navigationItem.rightBarButtonItem = UIBarButtonItem(
            title: "Close",
            style: .done,
            target: self,
            action: #selector(closeTapped)
        )
        
        tableView.register(UITableViewCell.self, forCellReuseIdentifier: "cell")
    }
    
    @objc private func closeTapped() {
        dismiss(animated: true)
    }
    
    override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return logs.count
    }
    
    override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath)
        let log = logs[indexPath.row]
        
        let dateFormatter = DateFormatter()
        dateFormatter.dateFormat = "HH:mm:ss"
        
        cell.textLabel?.numberOfLines = 0
        cell.textLabel?.font = .systemFont(ofSize: 12)
        cell.textLabel?.text = """
        [(dateFormatter.string(from: log.timestamp))] (log.result)
        (log.url)
        """
        
        return cell
    }
}

#endif

性能优化与最佳实践

性能优化


// MARK: - 路由性能优化

/// 路由缓存管理
public final class RouteCacheManager {
    
    public static let shared = RouteCacheManager()
    
    /// ViewController 缓存
    private var viewControllerCache: NSCache<NSString, UIViewController>
    
    /// 路由匹配结果缓存
    private var matchResultCache: NSCache<NSString, RouteMatchResult>
    
    private init() {
        viewControllerCache = NSCache()
        viewControllerCache.countLimit = 10  // 最多缓存10个VC
        
        matchResultCache = NSCache()
        matchResultCache.countLimit = 100  // 最多缓存100个匹配结果
    }
    
    /// 缓存的路由匹配结果
    class RouteMatchResult: NSObject {
        let pattern: String
        let handler: Any
        
        init(pattern: String, handler: Any) {
            self.pattern = pattern
            self.handler = handler
        }
    }
    
    /// 获取缓存的ViewController
    public func cachedViewController(for key: String) -> UIViewController? {
        return viewControllerCache.object(forKey: key as NSString)
    }
    
    /// 缓存ViewController
    public func cacheViewController(_ vc: UIViewController, for key: String) {
        viewControllerCache.setObject(vc, forKey: key as NSString)
    }
    
    /// 获取缓存的匹配结果
    public func cachedMatchResult(for path: String) -> RouteMatchResult? {
        return matchResultCache.object(forKey: path as NSString)
    }
    
    /// 缓存匹配结果
    public func cacheMatchResult(_ result: RouteMatchResult, for path: String) {
        matchResultCache.setObject(result, forKey: path as NSString)
    }
    
    /// 清理缓存
    public func clearCache() {
        viewControllerCache.removeAllObjects()
        matchResultCache.removeAllObjects()
    }
    
    /// 预热常用路由
    public func preloadCommonRoutes(_ paths: [String]) {
        for path in paths {
            // 预先匹配路由,缓存结果
            _ = URLRouter.shared.canOpen(path)
        }
    }
}

// MARK: - 路由预加载

public extension URLRouter {
    
    /// 预加载页面
    func preload(_ urlString: String, extra: [String: Any]? = nil) {
        guard let vc = viewController(for: urlString, extra: extra) else { return }
        
        // 触发 viewDidLoad
        _ = vc.view
        
        // 缓存
        RouteCacheManager.shared.cacheViewController(vc, for: urlString)
    }
    
    /// 批量预加载
    func preloadBatch(_ urlStrings: [String]) {
        DispatchQueue.global(qos: .utility).async {
            for urlString in urlStrings {
                self.preload(urlString)
            }
        }
    }
}

最佳实践总结


/*
 ════════════════════════════════════════════════════════════════════
                        路由最佳实践总结
 ════════════════════════════════════════════════════════════════════
 
 ✅ 架构选择
 ─────────────────────────────────────────────────────────────────────
 • 页面跳转 + DeepLink → URL Router
 • 组件服务调用       → Protocol-Class
 • 临时兼容/快速开发  → Target-Action
 • 复杂场景          → 混合方案
 
 ✅ URL设计规范
 ─────────────────────────────────────────────────────────────────────
 • 使用统一的 Scheme:myapp://
 • 路径使用小写 + 中划线:/product-detail
 • 参数命名使用驼峰:productId, pageSize
 • 保持路径简洁:最多3级 /a/b/c
 
 ✅ 安全性
 ─────────────────────────────────────────────────────────────────────
 • 敏感操作必须登录拦截
 • URL参数需要验证和过滤
 • 防止 URL 注入攻击
 • 外部 URL 来源校验
 
 ✅ 可维护性
 ─────────────────────────────────────────────────────────────────────
 • 路由路径定义常量化
 • 提供类型安全的扩展方法
 • 完善的日志和调试工具
 • 路由文档自动生成
 
 ✅ 性能优化
 ─────────────────────────────────────────────────────────────────────
 • 路由匹配结果缓存
 • 高频页面预加载
 • 按需注册,延迟加载
 • 避免在路由处理中做耗时操作
 
 ✅ 测试策略
 ─────────────────────────────────────────────────────────────────────
 • 路由注册测试
 • 路由匹配测试
 • 拦截器测试
 • DeepLink 集成测试
 
 */

// MARK: - 路由常量定义(推荐)

public enum RoutePaths {
    
    // 首页
    public static let home = "/home"
    
    // 商品
    public enum Product {
        public static let detail = "/product/:id"
        public static let list = "/products"
        public static let search = "/search"
        
        public static func detailPath(id: String) -> String {
            return "/product/(id)"
        }
    }
    
    // 购物车
    public enum Cart {
        public static let main = "/cart"
        public static let checkout = "/checkout"
    }
    
    // 订单
    public enum Order {
        public static let list = "/orders"
        public static let detail = "/order/:id"
        
        public static func detailPath(id: String) -> String {
            return "/order/(id)"
        }
    }
    
    // 用户
    public enum User {
        public static let login = "/login"
        public static let register = "/register"
        public static let center = "/user"
        public static let settings = "/user/settings"
    }
    
    // 通用
    public enum Common {
        public static let webView = "/webview"
        public static let share = "/share"
    }
}

// MARK: - 使用常量

class SomeVC: UIViewController {
    
    func example() {
        // ✅ 使用常量
        UnifiedRouter.shared.navigate(to: RoutePaths.Product.detailPath(id: "123"))
        
        // ❌ 避免硬编码
        // UnifiedRouter.shared.navigate(to: "/product/123")
    }
}

总结


┌─────────────────────────────────────────────────────────────────────┐
│                    iOS 路由机制核心要点                              │
├─────────────────────────────────────────────────────────────────────┤
│                                                                     │
│  🔗 URL Scheme                                                      │
│     • 应用间跳转 & DeepLink 基础                                    │
│     • Universal Links 更安全                                        │
│     • 配置简单,接入成本低                                          │
│                                                                     │
│  🛣️ URL Router                                                      │
│     • 适合页面跳转场景                                              │
│     • 支持 DeepLink、Push 通知                                      │
│     • 动态化能力强                                                  │
│     • 缺点:参数传递受限                                            │
│                                                                     │
│  🎯 Target-Action                                                   │
│     • 无需注册,运行时调用                                          │
│     • 适合组件能力暴露                                              │
│     • 支持复杂参数传递                                              │
│     • 缺点:硬编码,无编译检查                                      │
│                                                                     │
│  📋 Protocol-Class                                                  │
│     • 类型安全,编译期检查                                          │
│     • 适合服务层调用                                                │
│     • IDE 支持好                                                    │
│     • 缺点:协议维护成本                                            │
│                                                                     │
│  🔀 混合方案(推荐)                                                 │
│     • URL Router: 页面跳转 + DeepLink                               │
│     • Protocol: 服务调用 + 数据获取                                 │
│     • Target-Action: 兼容 & 补充                                    │
│                                                                     │
└─────────────────────────────────────────────────────────────────────┘

路由机制是组件化架构的核心基础设施,选择合适的路由方案并做好设计,能大大提升项目的可维护性和开发效率。


参考资源:

JLRoutesCTMediatorMGJRouterBeeHiveApple – Defining a Custom URL SchemeApple – Universal Links

© 版权声明
THE END
如果内容对您有所帮助,就支持一下吧!
点赞0 分享
评论 抢沙发

请登录后发表评论

    暂无评论内容