在組件化通訊方案的設計之初,儘管咱們是純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。
其餘使用方法見:
參考資料: