jsdw_ios/QuickLocation/Tool/Router/AppRouter.swift

219 lines
7.6 KiB
Swift

//
// 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)
}
}