Swift 路由和模块通信解耦工具和规范。 可以让模块间无耦合的调用服务、页面跳转。
- 支持 cocopods 组件化开发.
- 无需注册URL,采用runtime来实现
target-action
形式函数调用. - 内置URL重定向模块.
- 内置日志模块.
- 支持模块自定义.
- 内置断言,可切换语言.
- 路由类支持
UIApplicationDelegate
管理. - 优先支持swift.
-
iOS 8.0+ / macOS 10.10+ / tvOS 9.0+ / watchOS 2.0+
-
Swift 4.x
pod 'Khala'
有部分内容无法准确定义,在此个人擅自定义以下名词.
- 路由类: 负责接收路由事件的
NSOBject
类. - 路由函数: 路由类中 负责接收路由事件的函数.
-
URL
在Khala中,最原始的URL结构为:
scheme://[route class]/[route function]?key1=value1&key2=value2
但是你可以编写重定向规则来实现复杂的URL结构,与权限控制.
-
首先我们定义2个独立的路由类文件, 并且将其分别封装至2个pod库中.
该部分内容可以下载示例工程体验.
-
AModule.swift
import UIKit import Khala @objc(AModule) @objcMembers class AModule: NSObject { func doSomething(_ info: [String: Any]) -> String { return description } func server(_ info: [String: Any]) -> Int { guard let value = info["value"] as? String, let res = Int(value) else { return 0 } return res } func forClosure(_ closure: KhalaClosure) { closure(["value": #function]) } func forClosures(_ success: KhalaClosure, failure: KhalaClosure) { success(["success": #function]) failure(["failure": #function]) } }
-
BModule.swift
import UIKit import Khala @objc(BModule) @objcMembers class BModule: NSObject { func doSomething(_ info: [String: Any]) -> String { return description } }
-
-
通过URL执行路由函数: Khala
-
普通调用:
// 2. 不保持参数类型,(url中参数类型皆为String) let value = Khala(str: "kl://AModule/server2?value=64")?.call() as? Int print(value ?? "nil") /// Print: 64
-
异步调用:
/// 单个block调用 Khala(str: "kf://AModule/forClosure")?.call(block: { (item) in print("forClosure:", item) }) /// Print: forClosure: ["value": "forClosure"] // /// 多个block调用 Khala(str: "kf://AModule/forClosures")?.call(blocks: { (item) in print("forClosures block3:", item) },{ (item) in print("forClosure block4:", item) }) //Print: forClosures block3: ["success": "forClosures(_:failure:)"] //Print: forClosure block4: ["failure": "forClosures(_:failure:)"] /// or Khala(str: "kf://AModule/forClosures")?.call(blocks: [{ (item) in print("forClosures block1:", item) },{ (item) in print("forClosure block2:", item) }]) //Print: forClosures block1: ["success": "forClosures(_:failure:)"] //Print: forClosure block2: ["failure": "forClosures(_:failure:)"]
-
UIKit/AppKit 扩展调用:
let vc = Khala(str: "kl://BModule/vc?style=0")?.viewController
-
-
路由通知 KhalaNotify
可以使用该类型来执行多个已缓存路由类中的同名函数.
// 缓存 AModule 与 BModule 路由类. Khala(str: "kl://AModule")?.regist() Khala(str: "kl://BModule")?.regist() // 执行通知 let value = KhalaNotify(str: "kl://doSomething?value=888")?.call() print(value ?? "") // Print: [<BModule: 0x60000242f230>, <AModule: 0x600002419d10>]
通知只能发送至已被缓存的路由类中. 缓存路径: KhalaClass.cache
-
路由注册
在 Khala中我提供了以下接口来抽象 KhalaClass.cache:
/// 注册路由类, 等同于Khala(str: "kl://AModule/doSomething") func register() -> Bool // 取消注册路由类, 等同于 KhalaClass.cache["AModule"] = nil func unregister() -> Bool // 取消全部注册路由类, 等同于 KhalaClass.cache.removeAll() func unregisterAll() -> Bool // 批量注册遵守Protocol协议的路由类: Khala.regist(protocol: Protocol)
-
URL重定向: KhalaRewrite
若开发者需要自定义路由解析规则或重定向路由函数,这部分则尤为重要.
-
构造规则:
let filter = RewriteFilter { if $0.url.host == "AModule" { var urlComponents = URLComponents(url: $0.url, resolvingAgainstBaseURL: true)! urlComponents.host = "BModule" $0.url = urlComponents.url! } return $0 }
-
添加至全局规则池
Khala.rewrite.filters.append(filter)
-
请求调用
let value = Khala(str: "kl://AModule/doSomething")?.call() print(value ?? "nil") /// Print: <BModule: 0x6000026e2800>
-
-
UIApplicationDelegate 生命周期分发
部分组件往往依赖于主工程中的AppDelegate
中部分函数.
- 在
Khala
中,需要显式的在主工程中的AppDelegate
调用与处理相关逻辑. - 服务类需要遵守
UIApplicationDelegate
协议.
主工程AppDelegate
:
@UIApplicationMain
class AppDelegate: UIResponder,UIApplicationDelegate {
var window: UIWindow?
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
let list = Khala.appDelegate.application(application, didFinishLaunchingWithOptions: launchOptions)
return true
}
}
组件中服务类:
@objc(AModule) @objcMembers
class AModule: NSObject,UIApplicationDelegate {
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
print("AModule.didFinishLaunchingWithOptions")
return true
}
}
-
日志模块: KhalaHistory
每一份url请求都将记录至日志文件中, 可以在适当的时候提供开发者便利.
-
开启日志(默认关闭)
Khala.isEnabledLog = true // or Khala.history.isEnabled = true
-
文件路径:
/Documents/khala/logs/
-
文件内容: 日期 + 时间 + URL + 参数
2018-12-01 02:06:54 kl://SwiftClass/double? {"test":"666"} 2018-12-01 02:06:54 kl://SwiftClass/double {"test":"666"}
-
-
扩展机制: KhalaStore
khala 库中提供了一个空置的类[KhalaStore]用于盛放路由函数对应的本地函数.来简化本地调用复杂度的问题.
extension KhalaStore { class func aModule_server(value: Int) -> Int { return Khala(str: "kf://AModule/server", params: ["value": value])!.call() as! Int } } @objc(AModule) @objcMembers class AModule: NSObject { func server(_ info: [String: Any]) -> Int { return info["value"] as? Int ?? 0 } } let value = KhalaStore.aModule_server(value: 46)
ps: KhalaStore 扩展文件建议统一放置.
-
断言机制
为方便开发者使用,添加了部分场景下断言机制,示例:
khala.iOS Fatal error: [Khala] 未在[AModule]中匹配到函数[server], 请查看函数列表:
0: init
1: doSomething:
2: vc
关闭断言(默认开启):
Khala.isEnabledAssert = false
- 缓存机制: KhalaClass.cache
- 当路由第一次调用/注册路由类时,该路由类将被缓存至 KhalaClass.cache 中, 以提高二次查找性能.
- 当路由类实例化时,该路由类中的函数列表将被缓存至 KhalaClass().methodLists中, 以提高查找性能.
-
路由类
限制:
-
路由类必须继承自
NSObject
-
需要添加
@objc(class_name)
要防止编译器移除该类.编译器会在编译时检查swift文件中未被调用的类,并移除.(>= swift 3.0)
示例:
// 推荐 @objc(AModule) @objcMembers class AModule: NSObject { func server1(_ info: [String: Any]) -> Int { ... } func server2(_ info: [String: Any]) -> Int { ... } } // 也行 @objc(BModule) class AModule: NSObject { @objc func server1(_ info: [String: Any]) -> Int { ... } @objc func server2(_ info: [String: Any]) -> Int { ... } }
-
-
路由函数
限制:
-
不支持函数重载.例如:
@objc(AModule) @objcMembers class AModule: NSObject { func server(_ info: [String: Any]) -> Int { ... } func server(_ info: [String: Any], closure: KhalaClosure) -> Int { ... } }
缘由: khala 缓存了路由类中的函数列表, 键名为第一个
:
前的字符串. -
推荐第一个参数采用匿名参数,方便阅读.
-
参数格式只支持
-
单个:
[AnyHashable: Any]
, 无顺序要求: -
多个:
KhalaClosure
, 有顺序要求:typealias KhalaClosure = @convention(block) (_ useInfo: [String: Any]) -> Void
示例:
@objc(AModule) @objcMembers class AModule: NSObject { func server1() -> Int { ... } func server2(info: [String: Any]) -> Int { ... } func server3(_ info: [String: Any]) -> Int { ... } func server4(_ info: [String: Any], closure: KhalaClosure) -> Int { ... } func server5(_ info: [String: Any], success: KhalaClosure,failure: KhalaClosure, complete: KhalaClosure) -> Int { ... } func server6(_ success: KhalaClosure,failure: KhalaClosure, info: [String: Any], complete: KhalaClosure) -> Int { ... } }
缘由:
block
为结构体类型,无法抽象出基类或者协议.[String: Any]
会适当的插入[KhalaClosure]
中组成参数列表.ps: 调用方
KhalaClosure
数目需要比路由函数多或者持平.否则会触发断言. -
-
-
Khala 初始化函数
params
中的参数会保持传入的类型,例如传递UIImage
等对象.
public class Khala: NSObject { public init(url: URL, params: [AnyHashable: Any] = [:]) { ... } public init?(str: String, params: [AnyHashable: Any] = [:]) { ... } }
-
自定义 重定向模块
-
继承
KhalaRewrite
协议. -
替换重定向模块
Khala.rewrite = CustomRewrite()
-
-
自定义 日志模块
-
继承
KhalaHistory
协议. -
替换日志模块
Khala.history = CustomHistory()
-
- 完善 Objective-C 调用
- 完善demo示例.
- 日志模块采用mmap读写(解决crash部分日志未写入文件).
- 英文注释与文档.
- API Reference - 更详细的参考api文档.
- iOS路由(Khala)设计 - khala的选型与模组化中的角色担当.
- CTMediator: 由 Casa 创建的
Target-Action
形式解耦路由. - Routable: khala 的前身, 正式投入生产环境迭代2年.
- 星际争霸: khala 名称源自星际争霸背景设定中达拉姆星灵的主要宗教,它基于一种由信徒之间的灵能链接而形成的哲学。
linhay, is.linhay@outlook.com
Khala is available under the MIT license. See the LICENSE file for more info.