基于 WLRRoute 的 iOS 移动端路由设计

1,083 阅读4分钟

本文并不是要介绍什么是移动端的路由,如果对这个概念不了解的话可以看下前人的文章,这里是 传送门
之所以决定要在项目里面加入这个机制,是源于我们运营人员的一次偶然的需求,那就是他们希望能够通过活动页的H5链接直接打开APP,并且定位到指定的页面去,当然通过传统的方式也是可以一步一步的实现,只是需要做特殊处理罢了,具体就不多说了。
但我们想做的彻底一点,不如就此机会直接加入路由好了。没错,遇到问题当你想的比别人多一两步的时候就会看到一片新的天地。目前实现的功能已经不仅仅是运营人员所提及的那个小功能了,我们已经实现了通过短信url短连接的方式访问APP页面的功能,这个功能很早之前就有了,但是我们却一直没有加入。并且对现有的代码结构也带来了好的改变,那就是我们在进行页面跳转的时候,不需要再耦合具体的视图控制器,而是通过路由进行周转,这样的话可以大大降低代码的耦合度,当然这方面目前还没有推广开,因为大伙的编码习惯是比较难改变的。如果遇到一个强有力的领 导的话,说不定会推行这件事情。
我们的路由机制是建立在WLRRoute之上的,做这个第一步就是要搞清楚什么是页面对应的url,这里的url,并不是短连接里面所显示的那样子。比如m.vip.com/dcf6 这个是短信里的唯品会的链接,点击后会进入到他们的M站,而在进入M站后会提示我们是否要在软件中打开,所以这里的这个地址只是连接向唯品会M站的地址而已,通过后面所携带的dcf6参数再去他们的数据库中去查询取回所对应的APP中的路由地址。这个地址通常是 URL Schemes://url这样的格式,前面的URL Schemes是每个APP所独有的,比如我们的项目的URL Schemes是 abcmanager,而我们给首页一个地址叫做homepage的话,那这个首页所对应的地址就应该是 abcmanager://homepage, 只要在访问到M站的时候,M站返回这个地址,我们就可以打开我们的软件了。当然在这之前会弹出一个确认框,让你确认是否进入APP。比如下图。


屏幕快照 2017-04-07 上午11.06.31.png

搞清楚了这个页面所对应的路由是怎么回事就好办了,接下来我们给几乎能用的到的常用页面进行了路由的url注册,将它们写入了一个plist文件中,一种是带参数的,一种是不带参数的,如下图所示。


屏幕快照 2017-04-07 上午10.50.55.png

屏幕快照 2017-04-07 上午10.50.46.png


其中pjID是那个页面所携带的参数,为0-9之间的数字。

下面简要讲一下基于WLRRoute我们所做的二次封装。
先讲一下几个关键的类:
SpaceHomeRouter,继承于WLRRouter。功能就是注册url。
SpaceHomeRouteAnalysis,继承于NSObject,用以解析外接传入的路由信息。
SpaceHomeBaseRouteHandler,继承于WLRRouteHandler,用于快速的处理handler,而无需像demo中所展示的那样每一个都创建一个类。
最后就是RouterModel,用以承载plist文件中的数据。

具体代码如下:
SpaceHomeRouter

- (id)init{
    if (self = [super init]) {
        NSString *filePath = [[NSBundle mainBundle] pathForResource:@"Route" ofType:@"plist"];
        //字典数组
        NSArray *dicArray = [NSArray arrayWithContentsOfFile:filePath];
        //字典数组转化为模型数组
        _routeModelArray = [RouterModel mj_objectArrayWithKeyValuesArray:dicArray];
        _allHandlers = [NSMutableArray array];
    }
    return self;
}

- (void)registerAllHandlers{
    for (RouterModel *model in _routeModelArray) {
        SpaceHomeBaseRouteHandler *routeHandler = [SpaceHomeBaseRouteHandler routeHandlerWithClsName:model.clsName ClsTab:model.clsTabPage];
        [self registerHandler:routeHandler forRoute:[SHRouteBaseURL stringByAppendingString:model.clsUrl]];
        [_allHandlers addObject:routeHandler];
    }
}

SpaceHomeRouteAnalysis

- (void)handleURL:(NSURL *)url primitiveParameters:(NSDictionary *)primitiveParameters{
    UITabBarController *tabVC = (UITabBarController *)(AppDelegate *)[UIApplication sharedApplication].delegate.window.rootViewController;
    for (UINavigationController *nav in tabVC.viewControllers) {
        [nav popToRootViewControllerAnimated:YES];
    }
    NSLog(@"SiginCallBack");

    [[SpaceHomeRouter sharedSpaceHomeRouter] handleURL:url primitiveParameters:primitiveParameters targetCallBack:^(NSError *error, id responseObject) {
    } withCompletionBlock:^(BOOL handled, NSError *error) {
        NSLog(@"SiginHandleCompletion");
    }];
}

SpaceHomeBaseRouteHandler

+ (instancetype)routeHandlerWithClsName:(NSString *)clsName ClsTab:(NSString *)clsTab{
    return [[self alloc]initWithClsName:clsName clsTab:clsTab];
}

- (instancetype)initWithClsName:(NSString *)clsName clsTab:(NSString *)clsTab{
    if (self = [super init]) {
        _clsName = clsName;
        _clsTab = clsTab;
    }
    return self;
}

-(UIViewController *)sourceViewControllerForTransitionWithRequest:(WLRRouteRequest *)request{
    TabBarController *tabVC = (TabBarController *)[UIApplication sharedApplication].windows[0].rootViewController;
    CommonNavigationViewController *nav = (CommonNavigationViewController *)tabVC.selectedViewController;
    return nav;
}

-(UIViewController *)targetViewControllerWithRequest:(WLRRouteRequest *)request{    
    UIViewController * vc = [[NSClassFromString(_clsName) alloc]init];
    return vc;
}

-(BOOL)transitionWithWithRequest:(WLRRouteRequest *)request sourceViewController:(UIViewController *)sourceViewController targetViewController:(UIViewController *)targetViewController isPreferModal:(BOOL)isPreferModal error:(NSError *__autoreleasing *)error{
    if (_clsTab.intValue>0) {
        TabBarController *tabVC = (TabBarController *)[UIApplication sharedApplication].windows[0].rootViewController;
        tabVC.selectedIndex = _clsTab.intValue-1;
        return YES;
    }

    if (isPreferModal||![sourceViewController isKindOfClass:[CommonNavigationViewController class]]) {
        [sourceViewController presentViewController:targetViewController animated:YES completion:nil];
    }
    else if ([sourceViewController isKindOfClass:[CommonNavigationViewController class]]){
        CommonNavigationViewController * nav = (CommonNavigationViewController *)sourceViewController;
        [nav pushViewController:targetViewController animated:YES];
    }
    return YES;
}

最后在Appdelegate中的方法中加入下面一句代码就可以了。

- (BOOL)application:(UIApplication *)app openURL:(NSURL *)url options:(NSDictionary *)options {
    [[SpaceHomeRouteAnalysis sharedSpaceHomeRouteAnalysis]handleURL:url primitiveParameters:options];
}