// // AppRouter.swift // Alamofire // // Created by osell on 2023/2/7. // import Foundation import URLNavigator public typealias RouterParameter = [String : Any] public typealias CustomHandlerFactory = (String, RouterParameter?) -> Bool public typealias ContextHander = (String, RouterParameter) -> UIViewController? public protocol AppRouterProtocol: CustomStringConvertible { func setupRegisterRouter() func setupHandlerRouter() } @objcMembers public class AppRouter: NSObject { public static let shared = AppRouter() private let navigator: Navigator static var globalRouteHandler: CustomHandlerFactory? var rootViewController: UIViewController? { return UIViewController.topMost } var navigationController: UINavigationController? { return UIViewController.topMost?.navigationController } override init() { self.navigator = Navigator() super.init() } public func setup() { if let this = self as? AppRouterProtocol { this.setupRegisterRouter() this.setupHandlerRouter() } } } // MARK: - Register/Handler public extension AppRouter { /// 注册路由 /// - Parameters: /// - router: 路由 /// - factory: ViewControllerFactory class func register(_ router: RouterTarget, factory: @escaping ContextHander) { AppRouter.shared.navigator.register(router.url) { _, values, context in var values = values if let userInfo = context as? RouterParameter { values.merge(userInfo, uniquingKeysWith: { value1, value2 in value2 }) } return factory(router.url, values) } } /// 处理单个路由 /// - Parameters: /// - router: 路由 /// - factory: URLOpenHandlerFactory class func handle(_ router: RouterTarget, _ factory: @escaping CustomHandlerFactory) { AppRouter.shared.navigator.handle(router.url) { _, values, context in var values = values if let userInfo = context as? RouterParameter { values.merge(userInfo, uniquingKeysWith: { value1, value2 in value2 }) } return factory(router.url, values) } } /// 处理全局路由设置 /// - Parameter handler: CustomHandlerFactory class func globalRouteHandler(_ handler: CustomHandlerFactory?) { AppRouter.globalRouteHandler = handler } /// 是否注册过路由 /// - Parameter url: 路由url /// - Returns: true/false class func isRegisterRouter(_ url: String, userInfo: RouterParameter?) -> Bool { AppRouter.viewController(url, userInfo: userInfo) != nil } class func viewController(_ url: String, userInfo: RouterParameter?) -> UIViewController? { AppRouter.shared.navigator.viewController(for: url, context: userInfo) } } // MARK: - Push/Present public extension AppRouter { /// Router RouteTarget Push /// - Parameters: /// - router: RouterTarget /// - isHandler: 是否处理 /// - userInfo: 参数 /// - Returns: UIViewController @discardableResult class func push(_ router: RouterTarget, userInfo: RouterParameter? = nil) -> UIViewController? { var params = RouterParameter() if let parameters = userInfo { params = params.merging(parameters) { _, value2 in value2 } } guard AppRouter.globalRouteHandler?(router.url, params) == true else { return nil } return AppRouter.shared.navigator.push(router.url, context: params) } /// Router URLString Push /// - Parameters: /// - router: URL String /// - isHandler: 是否处理 /// - userInfo: 参数 /// - Returns: UIViewController @discardableResult class func push(_ url: String, userInfo: RouterParameter? = nil) -> UIViewController? { let url = url.addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed) ?? "" guard let urlURL = URL(string: url) else { return nil } var params = urlURL.queryParameters as RouterParameter if let parameters = userInfo { params = params.merging(parameters) { _, value2 in value2 } } guard AppRouter.globalRouteHandler?(url, params) == true else { return nil } return AppRouter.shared.navigator.push(url, context: params) } /// Pushes the view controller to the navigation controller stack. /// /// - note: It is not a good idea to use this method directly because this method requires all /// parameters. This method eventually gets called when pushing a view controller, so /// it's recommended to implement this method only for mocking. @discardableResult class func push(_ viewController: UIViewController, from: UINavigationControllerType? = nil, animated: Bool = true) -> UIViewController? { return AppRouter.shared.navigator.push(viewController, from: from, animated: animated) } /// Router RouteTarget Present /// - Parameters: /// - router: RouterTarget /// - isHandler: 是否处理 /// - userInfo: 参数 /// - Returns: UIViewController @discardableResult class func present(_ router: RouterTarget, userInfo: RouterParameter? = nil, completion: (() -> Void)? = nil) -> UIViewController? { var params = RouterParameter() if let parameters = userInfo { params = params.merging(parameters) { _, value2 in value2 } } guard AppRouter.globalRouteHandler?(router.url, params) == true else { return nil } return AppRouter.shared.navigator.present(router.url, context: params, completion: completion) } /// Router URLString Present /// - Parameters: /// - router: URL String /// - isHandler: 是否处理 /// - userInfo: 参数 /// - Returns: UIViewController @discardableResult class func present(_ url: String, userInfo: RouterParameter? = nil, completion: (() -> Void)? = nil) -> UIViewController? { let url = url.addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed) ?? "" guard let urlURL = URL(string: url) else { return nil } var params = urlURL.queryParameters as RouterParameter if let parameters = userInfo { params = params.merging(parameters) { _, value2 in value2 } } guard AppRouter.globalRouteHandler?(url, params) == true else { return nil } return AppRouter.shared.navigator.present(url, context: params, completion: completion) } /// Router URLString Present /// - Parameters: /// - viewController: viewController /// - animated: /// - completion: /// - Returns: UIViewController @discardableResult class func present(_ viewController: UIViewController, animated: Bool = true, completion: (() -> Void)? = nil) -> UIViewController? { return AppRouter.shared.navigator.present(viewController, animated: animated, completion: completion) } func popOrDismiss() { if navigationController?.viewControllers.count == 1 && navigationController?.presentingViewController != nil { navigationController?.dismiss(animated: true, completion: nil) } else { navigationController?.popViewController(animated: true) } } func popToRoot() { navigationController?.popToRootViewController(animated: true) } }