『ios』组件化-组件之间的跳转路由解耦

2021-03-03  本文已影响0人  butterflyer

公司业务模块越来越多,代码量增加,组件化是一个必然的趋势,最近看了公司的项目,想说下自己的一些理解。

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;
    }
  1. 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();
                }
            }];
        }
    
    };

进行上面的路由配置,就可以进行组件之间的跳转解耦。
后续会继续补充..

上一篇下一篇

猜你喜欢

热点阅读