URL Router初步认识

需求来源

  1. 页面跳转,主要是Controller的跳转,都是一些小的函数,并且需要创建目标controller的对象实例。希望将跳转逻辑集中在一个地方处理;并且controller之间能够解耦,不要相互持有。

  2. 服务化思路, 将服务端SOA架构引入客户端,对于服务和微服务,如何调用?采用PC互联网的经验,URL的方式是最成熟的。

  3. 组件化思路,分解日益庞大的APP客户端,如何调用组件的功能?函数、代理、block、KVO、通知...?URL纯字符的方式,解耦更彻底一点,可能更适合一点

第三方库: routable-ios

在gitHub中输入router参数,搜索结果中star排第一的开源库1323

功能1:打开ViewController

  1. 设置navigationController,这个只要设置一次,一个APP只有一个navigationController
UINavigationController *nav = [[UINavigationController alloc] initWithNibName:nil bundle:nil];
[Routable sharedRouter].navigationController = nav;
  1. 用map注册ViewController;格式(format)是 identifier/:key1/:key2,能够支持中文。
[[Routable sharedRouter] map:@"用户页面/:参数1/:qq" toController:[UserController class]];
  1. 使用的地方用open函数调用;格式(format)是 identifier/value1/value,能够支持中文。注意这里没有:
[[Routable sharedRouter] open:@"用户页面/dd/值2"];
  1. 目标ViewController重写函数initWithRouterParams(初始化函数)或者allocWithRouterParams(从故事版或者Xib中加载)
@implementation UserController

- (id)initWithRouterParams:(NSDictionary *)params {
  if ((self = [self initWithNibName:nil bundle:nil])) {
    NSLog(@"%@",params);
  }
  return self;
}

@end
@implementation UserController

+ (id)allocWithRouterParams:(NSDictionary *)params {
    UIStoryboard *storyboard = [UIStoryboard storyboardWithName:@"Storyboard" bundle:nil];
    StoryboardController *instance = [storyboard instantiateViewControllerWithIdentifier:@"UserController"];
    NSLog(@"%@",params);

    return instance;
}

@end

map时设定的key值和open时设定的value值都在字典参数params中了

Printing description of params:
{
    qq = "\U503c2";
    "\U53c2\U65701" = dd;
}

功能2:调用函数

  1. 注册map:
[[Routable sharedRouter] map:@"invalidate/:id" toCallback:^(NSDictionary *params) {
  [Cache invalidate: [params objectForKey:@"id"]]];
}];

2.调用open:

[[Routable sharedRouter] open:@"invalidate/5h1b2bs"];

功能3:打开外部链接

[[Routable sharedRouter] openExternal:@"http://www.youtube.com/watch?v=oHg5SJYRHA0"];

这部分的源码:

- (void)openExternal:(NSString *)url {
  [[UIApplication sharedApplication] openURL:[NSURL URLWithString:url]];
}

简评

  1. 能够router ViewController是特色

  2. 不能支持标准的URL格式是不足之处

  3. 打开外部链接功能,聊胜于无,作用不是很大

routable-ios

Routable, an in-app URL router for iOS and Android

Routable-iOS

第三方库:MGJRouter

蘑菇街提供的开源库,根据HHRouter改写的,主要功能是函数调用。在gitHub上的star数达到了513

  • 注册函数:
/**
 *  routerParameters 里内置的几个参数会用到上面定义的 string
 */
typedef void (^MGJRouterHandler)(NSDictionary *routerParameters);

/**
 *  注册 URLPattern 对应的 Handler,在 handler 中可以初始化 VC,然后对 VC 做各种操作
 *
 *  @param URLPattern 带上 scheme,如 mgj://beauty/:id
 *  @param handler    该 block 会传一个字典,包含了注册的 URL 中对应的变量。
 *                    假如注册的 URL 为 mgj://beauty/:id 那么,就会传一个 @{@"id": 4} 这样的字典过来
 */
+ (void)registerURLPattern:(NSString *)URLPattern toHandler:(MGJRouterHandler)handler;
  • 调用函数:
/**
 *  打开此 URL,带上附加信息,同时当操作完成时,执行额外的代码
 *
 *  @param URL        带 Scheme 的 URL,如 mgj://beauty/4
 *  @param parameters 附加参数
 *  @param completion URL 处理完成后的 callback,完成的判定跟具体的业务相关
 */
+ (void)openURL:(NSString *)URL withUserInfo:(NSDictionary *)userInfo completion:(void (^)(id result))completion;

withUserInfo其实是传一个字典过去,会添加到注册函数block的routerParameters参数中

completion在注册函数的block中执行,相当于实现了双向通信,或者说是双向调用。

  • URL可以满足scheme://host/path?key1=value1&key2=value2的标准形式

  • path中如果加:,可以传可变参数,会出现在注册函数block的routerParameters参数中

  • 没有专门针对ViewController的处理

  • open函数的三个参数URL,userInfo,completion都会以固定的key值传到注册函数block的routerParameters参数中

MGJRouter

蘑菇街 App 的组件化之路

蘑菇街 App 的组件化之路·续

第三方库:CTMediator

据说是当时跟蘑菇街Router一样火的一个开源库,gitHub上的star达到了595

  • 基本思想是将scheme://host/path?key1=value1&key2=value2的标准格式对应为native的Module,Class,SEL,parameters

  • scheme只是解析出来进行模块调用,用简单的字符串判断,是本模块的就往下执行,非本模块的就直接返回,啥也不干

  • 通过系统API- (id)performSelector:(SEL)aSelector withObject:(id)object;进行方法调用。调用结果通过一个block回传,参数是一个字典。

  • 类名通过APIClass _Nullable NSClassFromString(NSString *aClassName)获得

  • 方法名通过APISEL NSSelectorFromString(NSString *aSelectorName)得到

  • 分远程调用和本地调用两种,本地调用的实际action以native开头

  • 远程和本地调用,本质上都是用了runtime,是一样的。提供服务的类名都以Target_开头,方法都以action开头。

  • 不像普通的第三方库,用Pod中一句话就搞定。按照作者的意思,在真实的场景中,这里应该是三个不同repo,包括中间件、接口、实现三部分。在实际使用中,应该用最原始的源文件手动导入的方式。

Category目录在实际工程中是单独的一个repo,调用者通过依赖category这个repo来完成功能调度。一般来说是每一个业务对应一个category的repo。因此调用者需要调度哪个业务,就依赖哪个业务的category。category这个repo由对应提供服务的业务来维护。

CTMediator目录在实际工程中也是一个单独的repo,仅用于存放中间件。被每一个业务线各自维护的category repo所依赖。

DemoModule目录是实际提供服务的业务,这个在实际工程中也是一个单独的repo。这个repo不被任何人所依赖,这个repo通过target-action来提供被调度的功能,然后由category repo通过runtime调度。

CTMediator

iOS应用架构谈 组件化方案

如何选择?

routable-ios应该是最先出来的,其本意应该是将分散在APP各个地方ViewController的跳转集中起来,面向切片编程的思想。这方面是独特的。block调用的功能应该是后面顺应潮流而添加的,其处理方式也很简单粗暴,就是在其option参数中增加一个callback字段,如果有,就直接执行返回了。基本思路是callback或者ViewController,否则就是异常出错了。

HHRouter应该是参考routable-ios的实现原理,对于ViewController部分的功能进行了弱化,仅仅是取得这个对象就好了,没有跳转的具体实现。对于block部分的功能进行了增强,看起来更像标准的URL

MGJRouter是在HHRouter的基础上进一步发展。完全去除了已经沦为鸡肋的ViewController部分,继续增强block部分。以URL统一处理方法调用,不论是远程的还是本地的调用。先注册,再使用。

CTMediator是完全不同的思路。抛弃了URL统一管理的思想,用runtime代替注册,减少了一半的维护工作。不过对于类名和方法名的命名上面要注意一下。至于URL,也是做了一层封装,解析之后,仍然是统一为本地的runtime调用。iOS应用架构谈 组件化方案是作者推荐的文章,写得比较清楚。

Object-C选择CTMediator方案,管理集中的注册表是一件比较麻烦的事,用Runtime来自动完成,省心省力

  1. 模块内部还是直接使用方法调用的形式,简单直接。

  2. ViewController可以做成router的形式。难复用,并且还经常发生变化

  3. 跟IOS8之后的动态framework结合起来,中间件可以作为一个独立的基础模块,其他的模块对外接口都做成这种router形式,具体的实现注意一下类和方法的命名。

  4. 动态页面,由后台返回需要跳转的目标页面,采用URL的模式包装

  5. 如果考虑用framework进行隔离,归集调用列表并不是强需求,所以CTMediator的Category是不需要的。这部分主要是target和action名字的hardcode,在framework对外的h文件中进行说明就可以了。

Swift选择蘑菇街方案,protocol是核心,远程调用增加URL解析的过程,runtime不适合在swift中使用

  1. protocol分为协议本身,协议使用者,协议实现者三部分

  2. 考虑与framework结合,主程序可以调用framework,但是framework不能调用主程序,framework之间可以相互调用。这个特性需要考虑进去。

  3. 使用者需要知道协议内容,实现者也需要知道协议内容,实现者和使用者可能出现在主程序,也可能出现在framework中。从这个角度考虑,协议本身应该放在一个独立的framework中,使用者和实现者只要import一下这个framework就可以了。

  4. 每个协议都通过protocol的扩展提供默认实现。就算没有实现者,协议也能够跑起来。这部分内容和协议都放在一个模块中。这就是Object-C中“not found”部分。这是swift的语言福利,要好好使用。

  5. 协议用得最多的地方是代理模式,这是一对一的关系。所以像蘑菇街方案那样提供一个使用者和实现者的配对管理模块(Module Manager)是十分必要的。这个应该单独做一个framework,作用就像MGJRouter一样了。使用者和实现者通过这个模块互相知道对方,应该是这个模块的基本功能。

  6. 对于远程调用,URL按照framework or module://class or struct/fucntion?[key : value]这种模式编码是比较好的做法,类型全部是String。对于提供远程调用的framework,多一步解析URL的步骤,之后,都转入以Protocol为核心的本地调用模式。至于解析URL这部分功能,可以作为String的扩展工具,也可以放在一个单独的framework中作为基础功能被调用。

  7. 至于安全性,对于scheme部分作为字符串的匹配是应该做一下的。再进一步,可以对参数[key:value]部分做下加密,毕竟这部分是放在URL的query参数部分的。再进一步,可以把scheme://后面部分都做一下加解密。

** 如果考虑兼容性,MGJRouter是最有优势的,自由度最大。**

  • routable-ios要求“page/:id”的格式。

  • CTMediator要求“scheme://host/path/?[parameters]”的格式

  • 现在项目中URL的格式是"scheme://host?[parameters]",语言是Object-C,为了考虑兼容性,改动最小,决定采用MGJRouter并且只用于需要后台动态配置的页面。

  • 本地模块的组件化还没有开始做,目前还是采用类方法+单利进行接口封装的形式,先把复用模块先分出来再说。目前产品还需要支持iOS7,所以framework的应用也要延后。

iOS 组件化方案探索

组件化架构漫谈

iOS组件化思路-大神博客研读和思考

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

推荐阅读更多精彩内容