一个简单、清晰Swift路由方案

目前主流的路由方案主要有三个,URLRoute、Protocol-class、Target-Action。关于这三个方案的文章有非常多了,每个路由方案都是各有优缺点。

现在介绍的这个路由方案,是一个专门给 Swift 项目用的,简单的路由解决方案。利用了 Swift 枚举的特性,可以简单的将项目中的业务模块进行解耦。

前置介绍——枚举

Swift 中的枚举相较于 OC 中的枚举强大了太多了。OC 中枚举只能支持 Int,而在 Swift 中可以支持整形、浮点数、字符串、布尔等类型。

Swift 的枚举还支持关联值

1
2
3
4
5
6
7
8
9
10
enum People {
case buy(someThing: String)
}

let p = People.buy(someThing: "Food")

switch p {
case .bug(let thing):
print(thing)
}

简单地说,就是枚举可以传值。 这个简易路由就是利用枚举的这个特性来实现的。

路由介绍

JTRouterModuleEventProtocol

统一路由事件的协议,是一个空协议,是为了统一路由事件的类型。各个模块利用枚举实现这个协议。

1
2
3
4
5
6
7
/// 登录模块,对外暴露的接口
public enum JTLoginModuleRouterEvent: JTRouterModuleEventProtocol {
/// 打开登录页面
case openLoginVC
/// 根据用户id获取用户信息
case getInfo(uid: String)
}

JTRouterProtocol

路由核心协议,由各个模块实现对应方法,暴露自身模块能力。外界调用时,只需要将需要调用的事件枚举(继承自 JTRouterModuleEventProtocol)传入,并由对应模块进行处理。

1
2
3
4
5
6
7
8
/// 协议定义
public protocol JTRouterProtocol {
var identifier: String { get }

func canHandle(routerEvent: JTRouterModuleEventProtocol) -> Bool

func handle(routerEvent: JTRouterModuleEventProtocol, completion: (([String: Any]) -> Void)?) -> Bool
}

JTRouterManager

路由管理中心。各个模块将自己注册进来,manager 将模块服务存储起来,在使用时,manager 会寻找注册进来的路由服务,查找可以响应的服务来处理事件。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
public class JTRouterManager {
private var moduleServices: [JTRouterProtocol] = []

public static let shared = JTRouterManager()
private init() {}

public func registService(service: JTRouterProtocol) {
let isRegisted = moduleServices.contains { value in
return value.identifier == service.identifier
}

if !isRegisted {
moduleServices.append(service)
}
}

@discardableResult
public func handle(routerEvent: JTRouterModuleEventProtocol, completion: (([String: Any]) -> Void)?) -> Bool {
var temp: JTRouterProtocol?
for service in moduleServices {
if service.canHandle(routerEvent: routerEvent) {
temp = service
break
}
}

guard let handler = temp else {
return false
}

return handler.handle(routerEvent: routerEvent, completion: completion)
}
}

使用示例

定义模块内事件

1
2
3
4
5
6
7
/// 登录模块,对外暴露的接口
public enum JTLoginModuleRouterEvent: JTRouterModuleEventProtocol {
/// 打开登录页面
case openLoginVC
/// 根据用户id获取用户信息
case getInfo(uid: String)
}

模块实现路由协议并注册自身

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
public class JTLoginModuleRouterService: NSObject, JTRouterProtocol {
@objc public class func serverStart() {
JTLoginModuleRouterService.shared
}

public static let shared = JTLoginModuleRouterService()
public override init() {
super.init()

JTRouterManager.shared.registService(service: self)
}
}

public extension JTLoginModuleRouterService {
public var identifier: String {
return String(describing: self)
}

public func canHandle(routerEvent: JTRouterModuleEventProtocol) -> Bool {
return routerEvent is JTLoginModuleRouterEvent
}

@discardableResult
public func handle(routerEvent: JTRouterModuleEventProtocol, completion: (([String: Any]) -> Void)?) -> Bool {
guard let loginRouter = routerEvent as? JTLoginModuleRouterEvent else {
return false
}

switch loginRouter {
case .openLoginVC:
handleOpenLoginVC()
case .getInfo(let uid):
let callbackInfo = handleGetInfo(uid: uid)
completion?(callbackInfo)
}

return true
}
}

private extension JTLoginModuleRouterService {
private func handleOpenLoginVC() {
let vc = JTLoginMainViewController()

// do something
}

private func handleGetInfo(uid: String) -> [String: Any] {
// get infos
let result: [String: Any] = ["name": "jt"]
return result
}
}

外部调用示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
let routerEvent1 = JTLoginModuleRouterEvent.openLoginVC

JTRouterManager.shared.handle(routerEvent: routerEvent1) { info in
// do something with call back info

}

let routerEvent2 = JTLoginModuleRouterEvent.getInfo(uid: "id")

JTRouterManager.shared.handle(routerEvent: routerEvent2) { info in
// do something with call back info
// ["name": "jt"]
}

补充

因为模块内需要在项目启动时,注册自身路由服务到路由管理中心。但是,在 Swift 中 +(void)load 方法不能使用,这里还是使用了 OC 的类来进行协助。

1
2
3
4
5
6
7
8
9
10
11
12
// Objective-C
@implementation JTLoginModuleRouterService (Private)

+ (void)load {
[self serverStart];
}

+ (void)initialize {
// do something
}

@end

当然,由于这两个方法是 NSObject 类中声明的,所以我们的 Swift 类必须继承自 NSObject 或其子类。