-
Notifications
You must be signed in to change notification settings - Fork 0
进阶教程
本教程将详细介绍
RaRouter
的组成,以及如何基于RaRouter
自定义路由。本文基本不涉及 快速入门 的内容,建议先阅读快速入门,再阅读本教程。
RaRouter
采用 面向协议 的编程思想,核心内容由四个协议 + 一个类组成:
-
ModuleRouter
协议:用于定义模块。 -
RouterTableProtocol
协议:用于定义路由表。 -
RaRouter
协议:提供执行、注册路由的能力。 -
RouterRegister
协议:用于简化路由注册逻辑。 -
RouterFactory
类:内部使用[String : block]
存储注册过的路由。
定义模块时,需要同时使用 ModuleRouter
与 RouterTableProtocol
协议。
public protocol ModuleRouter {
associatedtype Table: RouterTableProtocol
}
ModuleRouter
协议用来定义模块,只要是遵守该协议的类型,RaRouter
就会认为它是一个 模块。
协议要求一个 RouterTableProtocol
类型的泛型,意味着这个模块既然需要路由,那么它就必须提供一个 路由表。至于这个泛型实质上的用途,将在 执行路由 中详细介绍。
public protocol RouterTableProtocol: Codable {
var url: String { get }
}
RaRouter
内部使用 [String : block]
存储路由(详细参考注册路由部分),Key
即为路由字符串,所以在定义模块时,模块的路由表是逃不开的东西。
为了避免路由字符串散乱存放、复制错误以及拼写错误等问题的发生,RaRouter
使用 RouterTableProtocol
协议来提供、封装硬编码的路由字符串。
协议要求的 url
属性即是供内部使用,用来读取路由地址的。
RaRouter
使用 RouterFactory
、RaRouter
以及 RouterRegister
共同完成路由的注册逻辑。
public class RouterFactory {
public static let shared = RouterFactory()
private init() {}
public lazy var doHandlerFactories: [String : DoHandlerFactory] = [:]
public lazy var resultHandlerFactories: [String : ResultHandlerFactory] = [:]
public lazy var viewControllerHandlerFactories: [String : ViewControllerHandlerFactory] = [:]
}
RouterFactory
是一个 单例 类,其没有继承任何的父类。内部使用三个字典属性存储默认提供的三种路由操作。
路由的存取本质上就是对字典属性的读取操作。
public protocol RouterRegister: class {
static func register()
}
在 RaRouter
的早期版本中,需要对每个模块单独执行注册操作,在模块数量较多的情况下代码显得十分“冗余”且不易维护、扩展。每新依赖一个组件,就需要添加对应的注册方法;删除组件时则需要删除对应的注册方法,十分麻烦。
RouterRegister
协议就是用来简化上述注册流程的一部分。
RouterRegister
协议要求遵守对象必须是一个类。这样,在程序启动时,我们就可以获取所有类列表,遍历判断其是否遵循 RouterRegister
,进而执行协议要求的 register()
方法,执行路由的注册代码。
这些操作被封装成 initialize()
方法,定义在了 RaRouter
协议的扩展中,具体的代码可以查看:Register Module
经过一系列的封装,现在我们只需要一行代码就可以完成路由的注册,并且依赖、移除模块时,不需要对注册操作有任何额外的修改
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
Router<Modules>.initialize()
return true
}
RaRouter
协议是 RaRouter
组件的 “万金油”。其完整协议参考:执行路由
在路由的注册过程中,该协议担任了两个任务:
- 向
RouterFactory
单例写入数据。 - 提供
initialize()
方法,简化注册流程(在RouterRegister
已经提及)。
我们在 Router+Register.swift
文件中,对 RaRouter
协议进行扩展,其中提供了向 RouterFactory
单例写入数据的方法:
// MARK: - Register With String URL
public extension RaRouter {
static func register(for url: String, _ factory: @escaping DoHandlerFactory) {
RouterFactory.shared.doHandlerFactories[url] = factory
}
static func register(for url: String, _ factory: @escaping ResultHandlerFactory) {
RouterFactory.shared.resultHandlerFactories[url] = factory
}
static func register(for url: String, _ factory: @escaping ViewControllerHandlerFactory) {
RouterFactory.shared.viewControllerHandlerFactories[url] = factory
}
}
执行路由也是 RaRouter
协议的主场。其完整定义如下所示:
public protocol RaRouter {
associatedtype Module: ModuleRouter
}
协议要求了一个 ModuleRouter
类型的泛型,其有两个用途:
- 通过限制
Module
的具体类型,方便封装模块所提供的操作。 - 简化路由的执行、注册逻辑。
关于第一点,已经在 定义模块 中有所提及,在扩展 RaRouter
协议封装方法时,使用 where
关键字限制 Module
的具体类型,可以达到 命名空间 的效果,避免在执行路由时,自动补全过多,不方便查找 以及 方法重名等问题的发生。
基于该泛型的存在,RaRouter
进一步封装了执行、注册路由的方法,修改 table
参数为 Module.Table
类型,使其可以直接调用 ModuleRouter.Table
中封装的枚举值作为路由地址。
以 getResult
操作为例:
public extension RaRouter {
static func getResult(_ table: Module.Table, param: Any? = nil) throws -> Any? {
guard let factory = RouterFactory.shared.resultHandlerFactories[table.url] else {
throw RouterError.notHandler(url: url)
}
return try getResult(table.url, param: param)
}
static func register(for table: Module.Table, _ factory: @escaping ResultHandlerFactory) {
RouterFactory.shared.resultHandlerFactories[table.url] = factory
}
}
同时,项目中还保留了直接使用 String
类型注册、执行路由的能力,方便通过接口、JSON 文件等方式动态执行、注册路由。
RaRouter
中默认提供了一个遵守 RaRouter
协议的类型:Router
,其声明如下所示:
public enum Router<Module: ModuleRouter>: RaRouter { }
如无特殊需要,可以直接在该类型的基础上编写路由相关内容。
RaRouter
中还提供了一个 Global
类型。其遵守 ModuleRouter
协议,可理解为一个全局模块。其声明如下所示:
public enum Global: ModuleRouter {
public typealias Table = RouterTable
public enum RouterTable: String, RouterTableProtocol {
public var url: String { rawValue }
case none = "mbc://global/none"
}
}
该类型提供了在 无法使用具体模块类型调用 RaRouter
协议 的场景下,执行、注册某些路由操作的能力。
一般用来配合接口、JSON 文件等方式动态执行、注册路由。
基于面向协议的高扩展性,您可以定义自己的 Router
类型,或扩展 RaRouter
,提供更多种类的操作。
在某些情况下,您可能不需要组件自带的 Router
类型,或想将其定义为一个类。
只需要遵守 RaRouter
协议,您便可以声明自己的 Router
类型。
您还可以进一步扩展 RaRouter
协议。假如您的项目的模块中,经常定义某些返回 String
类型的方法,您便可以在 getResult
定义自己的 string()
方法:毕竟 viewController
操作也是在 getResult
的基础上进一步封装的。
您还可以定义自己的 RouterFactory
类型,提供对应于操作的字典属性:或者不用字典来存储
在自定义路由时,请牢记 “随心所欲”。
do
操作也好,viewController
操作也罢,无外乎就是在 RaRouter.getResult()
方法之上的一层封装。
而 RaRouter.getResult()
本质上也只是在操作 RouterFactory
中的字典属性。
一定要用字典来存储路由的映射关系吗?或许还有其他的办法。
您可以基于 RaRouter
进行简单的封装、改造。如果 RaRouter
能成为一个引子,帮助您构造出更符合您使用习惯、性能更高的路由组件,也是极好的。