在组件化通讯方案的设计之初,尽管咱们是纯Swift的组件化,我也一直难逃窠臼的想用注册(不管是注册协议仍是注册URL)的方式来解决问题,或者采用CTMediator
的Target-Action
方式,具体几种组件化方案的实现与利弊见文章:iOS 组件化 —— 路由设计思路分析🔥🔥 git
最开始设计的组件化解决方案,由于做为一个电商项目(才不是这个缘由),因此我仅采用了URL注册
的方式,我一直力求的它应该具有的特性以下:github
URL
的注册不须要手动调用其实3
、4
,已经跳出了组件路由设计的范畴。确切的说应该是模块解耦的范畴。swift
我把router
设计成单例,目的是保证其持有的["String": func]
的字典的惟一肯定性,其中func
做为闭包形式,传入参数返回ViewController
。那么注册环节就显而易见的为register(_ key: String, value: func)
,调用就会根据key
,执行闭包func
返回ViewController
,以此解决1
。api
在对其中key
的设计使用上,由于注册方与调用方都会用到,因此咱们将其写在公共组件内,又由于key
会附带传一些简单的值,因此我又加了一个方法对key
进行赋值处理操做,目的是为了保证第3
条。bash
在模块解耦问题的处理上,我设计了一个继承AppDelegate
方法的协议,暂称为AppLifeCycle
,同时添加了一些方法用于初始化注册操做。再又设计了一个脚本,能够将遵循AppLifeCycle
的实例生成一个plist
文件,这样在App
启动时候,一个方法调用就实现全部路由注册功能,以此解决4
、5
。服务器
对于第7
点,在设计之初由于公司尚未服务器端动态下发的功能,因此又加了中间件作fallBack
处理(固然也都没用上)。闭包
虽然原有的路由设计与模块解耦方案已经支持现阶段业务需求,可是使用上过于复杂,不够友好,并且也没用上多少swift
的特性,反而这些实现,若是用Objective-c
实现起来,会更方便一些,好比脚本生成plist
,OC
均可以不须要。app
最近有同事在对路由作抽离精简,仅抽出router
部分,主要在接口设计上进行优化。我在看完后对一些功能点提了优化可能,后续一直的交流沟经过程中,忽然想到,我能够用Protocol Witness Table
来实现这个路由啊!ide
其原理是: swift
会维护一个Protocol Witness Table
, 此表会保存实现了protocol
协议的方法的指针地址,当咱们调用方法时,是经过获取对象的内存地址和方法的位移去查找的。组件化
因此咱们能够用一个协议定义入参,一个协议定义实现,同一个Enum
(建议使用的)去实现,便可实现功能。
这种方式相似于target-action
,无需注册,接口约定,还具备其余一些优势:
那么如此,咱们的路由设计的核心代码,以下:
public protocol MediatorTargetType {} // 用于接口定义,约束接口
public protocol MediatorSourceType { // 用于枚举实现
var viewController: UIViewController? { get }
}
复制代码
target
须要遵循的协议就这么些。
mediator
须要遵循的协议与实现:
public protocol SwiftyMediatorType {
func viewController(of target: MediatorTargetType) -> UIViewController?
}
extension SwiftyMediator: SwiftyMediatorType {
public func viewController(of target: MediatorTargetType) -> UIViewController? {
guard let t = target as? MediatorSourceType else {
print("MEDIATOR WARNINIG: \(target) does not conform to MediatorSourceType")
return nil
}
guard let viewController = t.viewController else { return nil }
return viewController
}
}
复制代码
以上便是核心代码。 经过接口收束,须要传入MediatorTargetType
,尝试转换成目标类型MediatorSourceType
,以此返回viewController
。
在使用中,咱们仍然须要一个公共的组件库,对路由目标进行定义。假设这个库叫MediatorTargets
,其内容以下:
public enum ModuleAMediatorType: MediatorTargetType {
case home(title: String)
case personal(color: UIColor)
}
复制代码
而后在咱们写的模块库中,此时咱们是路由目标的提供方,如3
中核心代码所示,咱们须要 让ModuleAMediatorType
再遵循协议MediatorSourceType
,以此支持ModuleAMediatorType
返回viewController
:
import SwiftyMediator
import MediatorTargets
extension ModuleAMediatorType: MediatorSourceType {
public var viewController: UIViewController? {
switch self {
case .home(let title):
let vc = UIViewController()
vc.view.backgroundColor = .green
vc.title = title
return vc
case .personal(let color):
let vc = PresentedViewController()
vc.view.backgroundColor = color
vc.title = "Presented"
return vc
}
}
}
复制代码
那么实现方的调用,只须要:
import MediatorTargets
import SwiftyMediator
let vc = Mediator.viewController(of: ModuleAMediatorType.home(title: "Home"))
复制代码
嗯,就是这么简单。
若是只作简单的模块间通讯,到这是足够的了, 主要的就是2个协议。
固然,有些时候咱们须要作一些动态化的路由策略,好比作一下动态路由下发。我也对SwiftyMediator
作了一些接口适配,使用方式以下:
MediatorTargetType
的协议ModuleAMediatorType
,继续遵循协议MediatorRoutable
,并实现协议:extension ModuleAMediatorType: MediatorRoutable {
public init?(url: URLConvertible) {
switch url.pattern {
case "sy://push":
self = .push(title: url.queryParameters["title"] ?? "default")
case "sy://present":
self = .present(color: UIColor.red)
default:
return nil
}
}
}
复制代码
SwiftyMediator
的func register(_ targetType: MediatorRoutable.Type)
,注册ModuleAMediatorType
SwiftyMediator
的func replace(url: URLConvertible, with replacer: URLConvertible)
方法便可url
的方式作路由:Mediator.push("sy://push?title=hahaha")
当须要实现动态化的时候,不可避免的要去注册,并且要实现协议中的枚举初始化。虽然有些不便,可是在总体的接口收束度上仍是挺不错的。相比较注册URL的方式来讲,这些注册就少不少了。
鉴于目前系统有比较全面的生命周期通知定义,并且不须要在模块中大量注册url,因此这部分功能目前在考虑是否须要添加。
虽然代码很简单,实现也很简单,可是跳出惯性思惟,再去尝试一样须要不少思考。
SwiftyMediator,欢迎star。
其余使用方法见:
参考资料: