TheRouter-iOS 轻量化路由中间件
TheRouter是货拉拉打造的一款一起支撑 Android 及 iOS 的轻量级路由中间件,在iOS端吸取了其他言语的特性,支撑 注解 功用,极大提升了路由在iOS端的运用体感。摒弃了传统ioser的target-action或protocol理念,向更广的后台或Android运用对齐。 Github传送门>>
一、Why TheRouter
跟着运用需求杂乱度愈来愈高,开发人员将App架构由本来简单的MVC变成MVP,MVVM等杂乱架构并按事务拆分独立服务。那么该如何解耦各层,解耦各个界面和各个组件,不管多么杂乱的情况下都能坚持“高内聚,低耦合”的特色?跟着痛点益发杰出,在iOS领域内产生了几种常见的中间件:
能够发现现在iOS领域并无一个统一的组件间通讯中间件,因为各个中间件完结底层原理都不相同形成了好坏点明显,相互之间难以搬迁,并且很难和Android、H5、苹果自身的Universal Links构成一个通用式通讯组件。因而,TheRouter-iOS它来了!
TheRouter 四大核心功用:
- 依靠注入:完结相似Java注解功用,在vc类或任意办法上标示即可完结路由注册;
- 硬编码消除:内置脚本会主动将注册的path转为静态字符串常量供事务运用;
- 动态化才能 :支撑增加重定向、拦截器等,也能够运用后台装备动态增加;
- 页面导航跳转才能:支撑常规vc或Storyboard的push/present跳转才能;
二、完结原理
2.1 模块描述
├── Classes
│ ├── TheRouter+Annotation.h
│ ├── TheRouter+Annotation.m // 路由注解器及Path功用扩展
│ ├── TheRouter.h
│ └── TheRouter.m // 路由库核心代码(增修改查,重定向/拦截器)
└── Resources
└── scan.py // 注解扫描及硬编码处理脚本(该脚本只会被引用不会参加编译和打包)
2.2 存储结构
在路由表中一条路由的存储会转为KeyPath
,在后续查找时经过valueForKeyPath
办法会更加高效。
例如:test://abc.com/login 在组件中存储如下:
@{
@"test":@{
@"abc-com":@{
@"login":@{
@"_handler":block, // 打开回调
@"_placeholder":array, // 占位符信息
@"_interceptor":block, // 拦截器回调
@"_redirectUrl":string, // 重定向链接
}
}
}
}
在打开该路由时流程如下:
2.3 注解式依靠注入
其实早在BeeHive
库中其实就已经有针对iOS注解完结的办法,BeeHive中其实是利用了__attribute((used, section("__DATA,"#sectname" ")))
将映射联系写入了Mach-O文件中,在启动时读取Mach-O文件再注册到路由表。但是这样做会产生很多的全局变量也会形成Mach-O文件过大,因而TheRouter做法是将映射联系存入提前建好的plist文件,注解宏界说只做一个标识,供给脚本扫描运用,后续再读取该plist将路由注册。
注解宏界说:
/// VC annotators, equivalent to call registController: clazzName storyBoard: nil path: routerPath
#define TheRouterController(routerPath,clazzName)
/// Storyboard VC annotators, equivalent to call registController: clazzName Storyboard: sbName path: routerPath
#define TheRouterStoryboardController(routerPath,sbName,clazzName)
/// Method annotator, equivalent to calling registSelector:selName targetClass:clazzName path:routerPath
#define TheRouterSelector(routerPath,selName,clazzName)
这儿分为了三种注解:
- TheRouterController:vc类型
- routerPath:子途径
- clazzName:vc类名
- TheRouterStoryboardController:支撑storyboard中的vc
- routerPath:子途径
- sbName:storyboard文件名
- clazzName:vc类名
(注:这儿storyboardID必须与类名共同)
- TheRouterSelector:事情类型
- routerPath:子途径
- selName:办法名
- clazzName:事情办法地点类名
vc类型/storyboard vc类型
经过vc类型注解的vc会主动创立该vc的实例,如果路由中有参数的话会主动经过kvc办法给vc对应特点赋值。
vc类型注解在调用时也能够指定跳转类型,路由中会主动获取当时适宜的navigationController进行push、present或presentInNavition等操作。
事情类型
经过事情类型注解的办法对办法名不做任何要求,但需求确保办法只需一个入参并且入参必须是TheRouterInfo
,该类中包含了本次调用路由时的一切信息,例如路由参数、回调block等。
事情类型注解的办法能够有回来值也能够没有,该回来值其实就是调用openURLString
或openPath
办法时所回来的值,能够依据事务场景同步回来或许经过TheRouterInfo
对象中的openCompleteHandler
异步回来结果。
事情类型注解的办法能够是类办法也能够是实例办法,如果是实例办法那么在每次调用时都会主动创立一个实例。后续会支撑指定实例回来,事务将能够更灵活的指定完结实例。
全体注解发现/注册流程如下:
2.4 Path硬编码处理
运用路由组件难免会产生很多的硬编码字符串,对读写都有很大的费事,因而在TheRouter中会将注册的路由Path
依据规则转化为静态常量
寄存在特定的头文件中,有些相似R.swift。
后续会支撑经过注解增加路由相关注释文档,并写入该头文件中便利查看路由的界说
全体如下流程:
三、运用介绍
Cocoapods 引进
pod 'TheRouter'
3.1 注解运用
step1
创立TheRouterAnnotation.plist文件,必须在MainBundle下。
step2
为项目创立一个Aggregate类型的target:
step3
在新建的target增加脚本:
图中实例脚本参数含义:
python3 $SRCROOT/../TheRouter/Resources/scan.py # 脚本途径
$SRCROOT/ # 参数1:扫描途径,一般为项目根目录
$SRCROOT/TheRouter/ # 参数2:途径界说头文件寄存目录 一般为寄存至公共模块
$SRCROOT/TheRouter/TheRouterAnnotation.plist # 参数3:TheRouterAnnotation文件途径
step4
在运用加载完结时注册host,在想要跳转的VC类上增加路由注解或创立对应模块的Service类,在Service中的办法上增加注解即可,例如:
注册该项意图host:
[TheRouter.shared registPathAnnotationsWithHost:@"hd://com.therouter.test"];
注册ViewController类型路由:
TheRouterController(test/vc, TestViewController)
@interface TestViewController : UIViewController
@end
注册事情类型路由:
#import "TestService.h"
#import "TheRouter_Mappings.h"
#import <TheRouter/TheRouter+Annotation.h>
@implementation TestService
TheRouterSelector(test/jump, jumpToTestVC, TestService)
+ (id)jumpToTestVC:(TheRouterInfo *)routerInfo {
UIViewController *vc = [TheRouter.shared openVCPath:kRouterPathTestVcVC
cmd:TheRouterOpenCMDPush
withParams:@{@"title":@"123"}
hanlder:^(NSString * _Nonnull tag, NSDictionary * _Nullable result) {
!routerInfo.openCompleteHandler ?: routerInfo.openCompleteHandler(tag, result);
}];
return vc;
}
@end
step5
在今后的开发中对路由进行增修改时编译一次创立好的target,会主意向TheRouterAnnotation.plist文件写入信息,并在指定的目录下生成TheRouter_Mappings.h文件,将此文件拖入对应模块即可:
3.2 拦截器和重定向
拦截器:
拦截器支撑全途径或通配符设置,如果回来YES
那么对应的路由事情能够正常履行,反之则会被拦截不会履行路由事情,如果该拦截器中需求履行异步操作也能够先回来NO
,等候异步任务完结后再调用continueHandle
即可
// 只需访问hd://com.therouter.test或其子途径 (hd://com.therouter.test/xxx) 都会进入该回调
[TheRouter.shared registInterceptorForURLString:@"hd://com.therouter.test/*" handler:^BOOL(TheRouterInfo * _Nonnull router, id _Nullable (^ _Nonnull continueHandle)(void)) {
NSLog(@"will execute router %@", router.URLString);
return YES;
}];
重定向:
重定向能够用来搬迁老途径或线上遇到问题时可快速更改至其他页面接受事务,能够经过后台下发映射联系来动态增加。
// 访问 hd://test.com/test 时会走 hd://test.com/test/vc的事情
[TheRouter.shared registRedirect:@"hd://test.com/test" to:@"hd://test.com/test/vc"];
3.3 履行路由事情
路由支撑同步
和异步
回来,可依据事务灵活运用。在异步回来中事务也能够回来不同的tag来标记多种事务回来场景。
UIViewController *vc = [TheRouter.shared openVCPath:kRouterPathTestVcVC // 传入Path
cmd:TheRouterOpenCMDPush // 指定打开命令
withParams:@{@"title":@"123"} // 指定参数,这儿支撑对kvc赋值
hanlder:^(NSString * _Nonnull tag, NSDictionary * _Nullable result) {
!routerInfo.openCompleteHandler ?: routerInfo.openCompleteHandler(tag, result);
}];
四、写在最后
TheRouter
是现在iOS中一个功用比较全面的轻量级路由,结合了现在行业中各通讯组件的长处,也摒弃了大部分缺点,做成一个统一多场景组件。在运用中更符合现代编程习气,提升书写愉悦度的一起降低事务耦合度。
TheRouter
所运用的注解办法彻底解决了路由在iOS中手动注册的痛点,只能说一直注解一直爽,并提供了重定向
和拦截器
等动态接口,能够结合事务后台做事务逻辑降级操作或许动态跳转映射。
最后欢迎咱们在Github
issue
中提出需求,咱们评价后会尽快支撑,也欢迎任何人提供Pull Requests
,一起壮大咱们的TheRouter。
关于作者:王恺靖,货拉拉iOS司机端资深开发工程师,担任首要核心事务及根底架构作业。