背景
- 路由定义:项目中使用路由跳转时往往需要定义很多路由路径,如果直接使用硬编码,不利于代码维护,调用也不方便,如果使用枚举去定义每一个路由,随着路由路径加深,枚举数量增多,维护起来也不方便。
- 路由参数:如果无法在定义路由时明确路由所需的参数,那么实际在使用时只能传入Any,十分不方便。
Sourcery
Sourcery 是一个 Swift 代码生成的开源命令行工具,它 (通过 SourceKitten) 使用 Apple 的 SourceKit 框架,来分析你的源码中的各种声明和标注,然后套用你预先定义的模板 (进行代码生成。
我们可以定义一个枚举,通过sourcery来自动生成所需要的路由路径代码。同时为每个路由的路径节点明确参数。
路由枚举
enum SocialRouteModule: SourceryRouteGenerator {
case message(Message)
case xxx
...
}
extension SocialRouteModule {
enum Message {
/// 会话列表
case conversation
/// 消息首页
case home
/// 好友页面
case friend(Friend)
/// 单聊页面
case chat(String)
/// 站内信
case appMessage
enum Friend {
/// 搜索好友页面
case search
/// 好友设置页面
case setting(FriendInfo)
/// 发出好友请求
case request(Request, SearchUser)
/// 修改好友备注
case remark(String)
/// 通讯录
case contact
enum Request {
/// 好友请求列表
case list
}
}
}
}
我们使用这样若干个枚举来描述所需要管理的路由:
- 遵循协议
SourceryRouteGenerator
的被称为base路由,只有base路由枚举管理的枚举才会自动生成代码。 - 每个case代表路由路径上的一个节点
- 如果此节点不是叶子结点,那么它需要一个与其同名的枚举(首字母大写),并设定为其关联值,此枚举中的case作为此节点的子节点。结构类似一个多叉树。
- 如果此节点路由需要参数,为其添加一个关联值,作为路由参数,此参数类型应当遵循Codable。(两个关联值的顺序没有要求)
路由协议
public protocol Route {
var scheme: String { get }
var domain: String { get }
var path: String { get }
var route: String { get }
var params: Codable? { get }
init()
init(path: String)
}
协议定义了路由的相关参数。
自动生成的代码
extension _LFCRoute {
var account: Account { Account(path: path.appending("account/")) }
struct Account: Route {
var path: String
init(path: String) {
self.path = path
}
}
}
extension _LFCRoute.Account {
var login: Login { Login(path: path.appending("login/")) }
struct Login: Route {
var path: String
var _params: Optional<AccountInfo> = nil
var params: Codable? { _params }
func params(_ data: AccountInfo) -> Self {
var variableSelf = self
variableSelf._params = data
return variableSelf
}
init(path: String) {
self.path = path
}
}
}
...
模版会自动生成类似的代码,根据枚举的结构,生成若干对应的 struct,在使用路由时,可以通过链式语法一层一层调用所需要的路由,同时传入所需要的参数。
路由的使用
print(LFC.account.bind.google.params(.init(value: "er", bindId: "aa")).route)
print(LFC.profile.edit.name.route)
print(LFC.developer.fileExplorer.route)
print(LFC.friend.request.detail.params(.init(value: "asd", id: 999)).route)
print(LFC.account.bind.params(.init(value: "bindValue", bindId: "lfc_sdz")).route)
//输出:
/*
lfc://happy.account/bind/google?value=er&bindId=aa
lfc://happy.profile/edit/name
lfc://happy.developer/fileExplorer
lfc://happy.friend/request/detail?id=999&value=asd
lfc://happy.account/bind?value=bindValue&bindId=lfc_sdz
*/
相关代码:
你可以在这里找到相关代码和sourcery模版