『ios』组件化-组件之间的跳转路由解耦
公司业务模块越来越多,代码量增加,组件化是一个必然的趋势,最近看了公司的项目,想说下自己的一些理解。
1.组件之间的跳转解耦
很早之前吧,看过一些路由的源码,如JLRoutes,记得之前还写过一篇博客来记录自己的一些看法。
下面进入正题吧~
先来看下基本路由配置示例
/** Router 配置示例:
AAA = { 【scheme】
BModule = { 【模块名】
native = { 【页面类型 (native、h5、week)】
BViewController = { 【页面名称】
class = "B_ViewController"; 【页面类名】
xib = ""; 【xib名称,可选字段,如果该页面为xib创建】
interception = { 【拦截器】
class = "B_Interceptor"; 【拦截器类名】
..... 【可自定义扩展添加字段,在拦截器中自行处理】
};
};
};
};
};
*/
根据顺序来吧。
1.注册路由urlCheme和全局的拦截器
[RouterConfiguration setRouterUrlScheme:@"aa" host:@"bb.com"];
[RouterConfiguration setGlobalInterceptor:[[GlobalRouterInterceptor alloc] init]];
+ (void)setRouterUrlScheme:(NSString *)scheme host:(NSString *)host {
[self configuration].scheme = scheme;
[self configuration].host = host;
}
2.拿取路由配置文件,这样所有的配置vc就都加载到单例里
[RouterConfiguration addRouterPlistPaths:[self routePlist]];
路由配置是写到plist文件里
image.png
3.调用方式
self.router.build(url, topViewController).withCallback(^(id<RouterCallbackProtocol> callback) {
if(completion) completion(YES);
}).withParameter(params).navigation();
为什么可以用self调用呢,因为给nsobjct添加了一个get方法,来拿到router,get方法中返回的router当然就是全局的单例对象。
通过点语法来进行方法调用和把变量赋值给router对象。
- (id<Router> (^)(NSString *, UIViewController *))build {
return ^(NSString *url, UIViewController *vc) {
self.urlString = url;
self.lastVC = vc;
return self;
};
}
- (id<Router> (^)(NSDictionary<NSString *,id> *))withParameter {
return ^(NSDictionary *parame) {
self.parame = parame;
return self;
};
}
4.是不是很想知道.navigation中发生了什么操作。
因为之前已经通过点语法进行了变量赋值,现在直接可以调用navigation来进行相应的跳转。
[self routerBuildURL:self.urlString
parameter:self.parame
greenChannal:self.greenChannal
openMode:self.openMode
openAnimation:self.openAnimation
hidesBottomBar:self.hidesBottomBar
lastViewController:self.lastVC
completeblock:self.callbackBlcok];
5.一段一段的来分析routerBuildURL方法中发生了什么
上面的completeBlock传递的是我们的一个协议,里面包含了一些页面跳转状态的block,这是我觉得写得很好的一个地方。
/// 找到了 return YES 继续执行router, NO 停止执行router
@property (nonatomic, copy) BOOL(^onFound)(NSDictionary * _Nonnull pageInfoDict);
/// 未找到
@property (nonatomic, copy) void(^onLost)(void);
/// 跳转完成
@property (nonatomic, copy) void(^onArrival)(void);
/// 被拦截器拦截
@property (nonatomic, copy) void(^onInterrupt)(void);
下面处理的是打开网址的处理,success并且callback.anArrival实现的话,就进行调用成功的状态回调。如果lost则进行相应的处理。
RouterCallback *callback = nil;
if (completeBlock) {
callback = [[RouterCallback alloc] init];
completeBlock(callback);
}
if (mode == openExternal) {
NSURL *openUrl = [NSURL URLWithString:url];
if (@available(iOS 10.0, *)) {
[[UIApplication sharedApplication] openURL:openUrl options:@{} completionHandler:^(BOOL success) {
if (success && callback.onArrival) { callback.onArrival(); }
else if (!success && callback.onLost) { callback.onLost(); }
}];
} else {
BOOL success = [[UIApplication sharedApplication] openURL:openUrl];
if (success && callback.onArrival) { callback.onArrival(); }
else if (!success && callback.onLost) { callback.onLost(); }
}
return;
}
- RouterPostcard是作为整个路由中,记录路由信息的地方。
RouterPostcard *postcard = [[RouterPostcard alloc] initWithURLString:url withParameter:parameter];
postcard.greenChannal = isGreenChannal;
postcard.openMode = mode;
postcard.openAnimation = animation;
postcard.hidesBottomBar = hidesBottomBar;
比如记录了原始路由链接,host,path,query 打开方式,是否是绿色通道,是否隐藏tabbar,将要显示的控制器地址,来的控制器地址等。
@property (nonatomic, copy) NSURL *originalURL;
@property (nonatomic, copy) NSString *host;
@property (nonatomic, copy) NSString *path;
@property (nonatomic, copy) NSString *query;
@property (nonatomic, copy) NSString *scheme;
@property (nonatomic, copy) NSDictionary <NSString *, id>*parameter;
@property (nonatomic, strong) NSDictionary <NSString *, id>*interception;
@property (nonatomic, assign) BOOL greenChannal;
@property (nonatomic, assign) ZPMRouterOpenMode openMode;
@property (nonatomic, assign) BOOL openAnimation;
@property (nonatomic, assign) BOOL hidesBottomBar;
@property (nonatomic, weak) UIViewController *lastViewController;
/// 将要显示ViewController
@property (nonatomic, weak) UIViewController *tagerViewController;
@property (nonatomic, strong) ZPMRouterPostcard *originalPostcard;
7.下面是进行路由的重定向和对路由的传进来的原始链接进行解析
AAA://BBB.com/resume/native/editresume
上面的链接解析之后
host == BBB.com
scheme == AAA
path = /resume/native/editresume
然后去全局配置单例configuration中,用我们上面拿到的host scheme path 一步一步的拿到我们最终跳转需要的controller的名字。
// 重定向
Class replaceClass = [ZPMRouterConfiguration getPathReplaceClass];
if ([replaceClass respondsToSelector:@selector(routerPathReplace:)]) {
postcard = [((Class<ZPMRouterPathReplaceProtocol>)replaceClass) routerPathReplace:postcard];
}
__block NSDictionary *pageInfo = nil;
NSArray <NSString *>*pathComponents = [postcard.path pathComponents];
if (pathComponents.count != 0) {
pageInfo = [ZPMRouterConfiguration getRouterUrlDict];
}
[pathComponents enumerateObjectsUsingBlock:^(NSString * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
if (![obj isEqualToString:@"/"]) {
pageInfo = pageInfo[obj];
}
}];
postcard.interception = pageInfo[kZPMRouterInterception];
//是否实现了onfound方法,查找pageinfo是否能找到那个那个字典
@"class" : @"ResumeViewController"
if (callback.onFound) {
BOOL isContinue = callback.onFound(pageInfo);
if (!isContinue) {
return;
}
}
//找到上一层的控制器
if ([lastViewController isKindOfClass:[UINavigationController class]]) {
lastViewController = ((UINavigationController *)lastViewController).topViewController;
}
if (lastViewController && [lastViewController isKindOfClass:[UIViewController class]]) {
postcard.lastViewController = lastViewController;
} else {
// 有一种情况是,特意使用拦截器来进行处理,所以允许lastViewController为空
router_Log(@"lastViewController 为空");
}
// 没有找到降级处理
void(^onLostHandle)(void) = ^() {
if (callback.onLost) { // 自定义降级回调
callback.onLost();
} else { // 没有自定义降级进入全局降级
Class degradeClass = [ZPMRouterConfiguration getDegradeClass];
if ([degradeClass instancesRespondToSelector:@selector(onLost:)]) {
[(id<ZPMRouterDegradeProtocol>)[degradeClass new] onLost:postcard];
}
}
};
if (!pageInfo) {
router_Log(@"查找页面信息失败,url = %@",postcard.path);
onLostHandle();
return;
}
//根据pageinfo中的信息,拿到controller实例,并赋值给postcard中的tagerView
UIViewController *targetViewController = [self findViewController:pageInfo];
postcard.tagerViewController = targetViewController;
9.查看是否有绿色通道和拦截器拦截
// 绿色通道跳过拦截器处理
if (postcard.greenChannal) {
onJumpHandle();
return;
}
// 全局拦截器与自定义拦截器
[self interceptorHandle:postcard complete:^(BOOL isContinue) {
if (isContinue) {
onJumpHandle();
} else {
if (callback.onInterrupt) {
callback.onInterrupt();
}
}
}];
在全局的拦截器中去查看是否需要拦截进行相应的操作。
- (void)interceptorHandle:(ZPMRouterPostcard *)postcard complete:(void(^)(BOOL isContinue))block {
id<ZPMRouterGlobalInterceptorProtocol> globalInterceptor = [ZPMRouterConfiguration getGlobalInterceptor];
[self excute:globalInterceptor postcard:postcard complete:^(BOOL isContinue) {
NSString *interceptorClass = postcard.interception[kZPMRouterInterceptorClass];
if (isContinue && interceptorClass) {
id<ZPMRouterInterceptorProtocol> customInterceptor = [[[self findClass:interceptorClass] alloc] init];
[self excute:customInterceptor postcard:postcard complete:block];
} else {
block(isContinue);
}
}];
}
- (void)excute:(id<ZPMRouterInterceptorProtocol>)obj postcard:(ZPMRouterPostcard *)postcard complete:(void(^)(BOOL isContinue))block {
if ([obj respondsToSelector:@selector(interceptorProcess:complete:)]) {
[obj interceptorProcess:postcard complete:block];
} else {
block(YES);
}
}
10.进行跳转
void(^onJumpHandle)(void) = ^() { // 跳转页面
if (!targetViewController) {
router_Log(@"创建【UIViewController】失败,pageInfo = %@",pageInfo);
onLostHandle();
return;
}
targetViewController.postcard = postcard;
[targetViewController setValuesForKeysWithDictionary:postcard.parameter];
id <ZPMRouterCustomTransitionProtocol> transition = [ZPMRouterConfiguration getCustomTransition];
// 自定义转场
if (transition && [transition respondsToSelector:@selector(customTransition:)] && [transition customTransition:postcard]) {
if (callback.onArrival) {
callback.onArrival();
}
return;
}
if (postcard.openMode == openPush) {
targetViewController.hidesBottomBarWhenPushed = postcard.hidesBottomBar;
[postcard.lastViewController.navigationController pushViewController:targetViewController animated:postcard.openAnimation];
if (callback.onArrival) {
callback.onArrival();
}
} else if (postcard.openMode == openPresent){
[postcard.lastViewController presentViewController:targetViewController animated:postcard.openAnimation completion:^{
if (callback.onArrival) {
callback.onArrival();
}
}];
}
};
进行上面的路由配置,就可以进行组件之间的跳转解耦。
后续会继续补充..