JLRoutes 路由跳转使用宝典

一、简介

JLRoutes 是一个带有简单的基于块的API的URL路由库。它旨在使您在应用程序中以最少的代码处理复杂的URL方案变得非常简单。

JLRoutes 可以很方便的处理不同 URL schemes 以及解析它们的参数,并通过回调 block 来处理 URL 对应的操作 , 是一个可以用于处理复杂跳转逻辑的三方库。

二、使用场景

  • 1、在日常开发中,push、present 出现在整个程序的各个地方,如果你想快速理清一个项目的整体逻辑,那会非常麻烦。大多数情况 , 你得找到代码目录,根据层级结构分出关系,然后找到对应的push位置,寻找下一级页面,如果本身项目的目录就非常乱,那么如果要了解一个项目的整体跳转逻辑,将会更加困难。
    如果能把整个项目的跳转逻辑都给抽取出来,单独放在一个类,模块化管理,那么思路就会清晰很多,甚至可以用 XMind 根据代码画出整个项目的树状图。

  • 2、如果所处公司存在多个app,app之间互相推荐互相跳转是再正常不过的需求,就类似于QQ、微信、第三方分享跳转等。如果用Appdelegate 原生方法进行拦截,所做的事至少得是判断 Scheme 是否匹配,想办法进入需要跳到的界面,如果要涉及传参,就更加麻烦。

  • 3、如果用户是从 PC端 识别二维码,或者通过链接想要进入 app 指定页面。

三、原理

JLRoutes 本质可以理解为:保存一个全局的字典,key 是 URL,value 是对应存放 block 的数组,URL 和 block 都会常驻在内存中,当打开一个 URL 时,JLRoutes 就可以遍历这个全局的字典,通过 URL 来执行对应的 block。下面是根据自己的理解画的层次图:

JLRoutes 层次解析图

具体理解:

  • 1、routeControllersMap 是全局的单例可变字典

  • 2、这个字典的 key 值对应一个标识,源码中称之为 scheme,为了不混淆,咱们就叫其为 JLRoutes 对象标识。这个标识对应的value 值为 routesController(JLRoutes类的对象:JLRoutes *routesController)。

  • 3、JLRoutes的对象(routesController)有很多属性,常用的有两个属性:

    • NSString *scheme:也就是上面所说的 JLRoutes对象标识,也就是说,此 value 值记录了自己的 key 值。
    • NSMutableArray *routes:此数组中存放了JLRRouteDefinition 对象。
  • 4、JLRRouteDefinition 对象为最终的具体模型,也就是说你注册的跳转逻辑的所有信息,都存在于这个模型中,包括要实施操作的handlerBlock(执行操作的block代码块)、scheme(JLRoutes对象标识)、pattern(模式)、priority(优先级)。

四、开发步骤

在正式进行 JLRoutes 开发之前,了解如何通过设置app的 URL Scheme由外部跳转到app?具体步骤我已在之前的一篇文章中详细说明了(如何自定义 URL Scheme 进行跳转),此处略过,不明白的可以先去大致了解下原理。

1、配置 URL Schemes

一个 app 可以对应多个 URL Schemes,如下图 info.plist 配置,在 Safari 中,只要输入 JLRouteSchemeOne://JLRouteSchemeTwo:// 都可以打开该 app,而 URL identifier 最好是保证其唯一性,这里咱们为app设置了2个 URL Schemes,是为了后面手动解析URL而做的准备。

配置 URL Schemes
2、注册 JLRoutes

首先 , 考虑的问题有两个,一是什么时候注册路由,二是在什么地方注册路由。

比如一个项目的 tabbarItem 有2个,那么这2个模块的跳转,并不是由一个 navigationController 来完成,所以考虑到这点,我们可以创建一个分类,将跳转逻辑放在其中,在初始化 tabbarController 时进行注册路由跳转逻辑。

注册路由的方式有很多种:

1、全局JLRoutes注册

[[JLRoutes globalRoutes] addRoute:@"取url内容值的标识" handler:^BOOL(NSDictionary<NSString *,id> * _Nonnull parameters) {
        
    return YES; // 一旦匹配,立即返回 YES
}];

此方法对应的JLRoutes对象标识为 JLRoutesGlobalRoutesScheme,由下述源码可知 , 用 globalRoutes 方式创建的JLRoutes对象,无论创建多少次,始终对应着同一个实例。也就是说,无论你调用上述方法多少次,尽管 @”取url内容值的标识” 和 block块内容不一样,最后都会执行第一次注册的内容。此方法和咱们要实现的2个 tabbarItem 对应2种跳转要求不合,因为咱们要求的是 block块中的 navigationController 为2个不同的实例对象。

2、自定义命名空间注册

[[JLRoutes routesForScheme:@"第一模块的标识"] addRoute:@"取url内容值的标识" handler:^BOOL(NSDictionary<NSString *,id> * _Nonnull parameters) {
        
    return YES; // 一旦匹配,立即返回 YES
}];

此注册方法所得的JLRoutes对象都是唯一的,而这才是咱们真正需要的。

3、定义优先级注册

[[JLRoutes globalRoutes] addRoute:@"取url内容值的标识" priority:1 handler:^BOOL(NSDictionary<NSString *,id> * _Nonnull parameters) {
    
    return YES; // 一旦匹配,立即返回 YES
}];

简单来说:如果不设置优先级,所有的注册优先级都为 0。

当标识了优先级进行注册后,JLRRouteDefinition 对象(最终模型)在 JLRoutes对象的 routes数组 中将进行排序,类似于选择排序,当通过route对象寻找到其 routes数组 后,将会遍历整个 routes数组,优先级高的 JLRRouteDefinition对象 将会被最先匹配,然后return YES,并停止遍历。

咱们暂时用不上这个优先级,就不进行过多讲述,因为咱们注册的3个跳转,每个对应的routes数组中元素仅为1个。

4、定义多个 "取url内容值的标识" 进行注册

[[JLRoutes globalRoutes] addRoutes:@[@"取url内容值的标识", @"取url内容值的标识"] handler:^BOOL(NSDictionary<NSString *,id> * _Nonnull parameters) {
    
    return YES; // 一旦匹配,立即返回 YES
}];

我们使用第二种方法进行注册:取url内容值的标识暂时为 nil

[[JLRoutes routesForScheme:@"JLRouteSchemeOne"] addRoute:nil handler:^BOOL(NSDictionary<NSString *,id> * _Nonnull parameters) {
    
    return YES;
}];

[[JLRoutes routesForScheme:@"JLRouteSchemeTwo"] addRoute:nil handler:^BOOL(NSDictionary<NSString *,id> * _Nonnull parameters) {
    
    return YES;
}];
3、点击跳转

在开始这两个 URL scheme 都添加进了 info.plist,并用第二种注册方法(自定义命名空间注册)进行注册,接下来这两个方法就可以进行跳转:

- (void)clickBtn {
    NSString *customURL = @"JLRouteSchemeOne://OneDetailViewController";
    [[UIApplication sharedApplication] openURL:[NSURL URLWithString:customURL]];
}

- (void)clickBtn {
    NSString *customURL = @"JLRouteSchemeTwo://TwoDetailViewController";
    [[UIApplication sharedApplication] openURL:[NSURL URLWithString:customURL]];
}
4、实现 openURL 方法

openURL方法在此处对所有的跳转进行拦截,手动解析处理,再交于JLRoutes。

手动解析URL:如果Scheme从网页跳转过来,拦截到的会变成小写,所以直接将 URL Scheme 拦截下来,转换小写进行判断。经过处理之后,交于JLRoutes进行解析,寻找具体的操作。

这里也解释了为什么在 info.plist 文件中,要设定2个不同的URL Scheme。当在2个不同模块中进行跳转点击时,这个方法是必经的,进行拦截,判断具体是哪个模块后交于JLRoutes解析。

// iOS 9.0前方法
- (BOOL)application:(UIApplication *)application openURL:(NSURL *)url sourceApplication:(NSString *)sourceApplication annotation:(id)annotation {
    
    NSLog(@"Calling Application Bundle ID: %@", sourceApplication);
    NSLog(@"URL scheme: %@", [url scheme]);
    NSLog(@"URL query: %@", [url query]);
    
    // 从浏览器打开时候会自动全部转成小写,而从应用内调用的话大小写不会变化
    // 为了方便判断所以统一转成小写来判断

    NSString *urlSchemeStr = [[url scheme] lowercaseString]; // url scheme 转换为小写的字符串
    NSLog(@"urlSchemeStr: %@",urlSchemeStr);
    
    if ([urlSchemeStr isEqualToString:@"jlrouteschemeone"]) {
        
        // 要和 info.plist 的 URL types 里面的一致
        return [[JLRoutes routesForScheme:@"JLRouteSchemeOne"]routeURL:url];
        
    } else if ([urlSchemeStr isEqualToString:@"jlrouteschemetwo"]) {
        
        // 要和 info.plist 的 URL types 里面的一致
        return [[JLRoutes routesForScheme:@"JLRouteSchemeTwo"]routeURL:url];
    }
    
    return YES;
    
}

// iOS 9.0后方法
- (BOOL)application:(UIApplication *)app openURL:(nonnull NSURL *)url options:(nonnull NSDictionary<UIApplicationOpenURLOptionsKey,id> *)options {
    
    NSLog(@"options: %@", options);
    
    NSLog(@"Calling Application Bundle ID: %@", [options objectForKey:@"UIApplicationOpenURLOptionsSourceApplicationKey"]);
    
    NSLog(@"URL scheme: %@", [url scheme]);
    NSLog(@"URL  host : %@", [url host]);
    NSLog(@"URL  query: %@", [url query]);
    
    // 从浏览器打开时候会自动全部转成小写,而从应用内调用的话大小写不会变化
    // 为了方便判断所以统一转成小写来判断
    
    NSString *urlSchemeStr = [[url scheme] lowercaseString]; // url scheme 转换为小写的字符串
    NSLog(@"urlSchemeStr: %@",urlSchemeStr);
    
    if ([urlSchemeStr isEqualToString:@"jlrouteschemeone"]) {
        
        // 要和 info.plist 的 URL types 里面的一致
        return [[JLRoutes routesForScheme:@"JLRouteSchemeOne"]routeURL:url];
        
    } else if ([urlSchemeStr isEqualToString:@"jlrouteschemetwo"]) {
        
        // 要和 info.plist 的 URL types 里面的一致
        return [[JLRoutes routesForScheme:@"JLRouteSchemeTwo"]routeURL:url];
    }
    
    return YES;
}
5、参数传递,以及 tabbarController 选中问题处理

参数传递需要进行一一对应

[[JLRoutes routesForScheme:@"JLRouteSchemeTwo"]addRoute:@"/:ViewController/:userID/:pass" handler:^BOOL(NSDictionary<NSString *,id> * _Nonnull parameters) {
    
    NSLog(@"parameters: %@",parameters);
    NSLog(@"userID: %@",parameters[@"userID"]);
    NSLog(@"pass: %@",parameters[@"pass"]);
    NSLog(@"-----第二模块-----");

    Class class = NSClassFromString(parameters[@"ViewController"]);
    [navVc pushViewController:[[class alloc]init] animated:YES];
    
    self.selectedIndex = 1; // 解决从app外跳转进来的 tabbar 选中问题
    
    return YES;
}];

点击方法如下:

- (void)clickBtn {
    NSString *customURL = @"JLRouteSchemeTwo://TwoDetailViewController/我是userID/我是pwd";
    // 中文传输需要进行转义
    customURL = [customURL stringByAddingPercentEncodingWithAllowedCharacters:[NSCharacterSet URLQueryAllowedCharacterSet]];
    [[UIApplication sharedApplication] openURL:[NSURL URLWithString:customURL]];
}

处理从网页等跳转过来,比如直接跳到第二模块的第二级控制器,实际上已经跳转了,tabbarItem还是选中的第一个。只需要在block块中处理一下 selectedIndex 就行。

参考链接:JLRoutes路由跳转

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,222评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,455评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,720评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,568评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,696评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,879评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,028评论 3 409
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,773评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,220评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,550评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,697评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,360评论 4 332
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,002评论 3 315
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,782评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,010评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,433评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,587评论 2 350

推荐阅读更多精彩内容