IOS开发路上的故事架构层面的设计iOS开发

URL Router初步认识

2016-11-04  本文已影响4855人  勇往直前888

需求来源

  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中执行,相当于实现了双向通信,或者说是双向调用。

MGJRouter

蘑菇街 App 的组件化之路

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

第三方库:CTMediator

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

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是最有优势的,自由度最大。**

iOS 组件化方案探索

组件化架构漫谈

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

上一篇下一篇

猜你喜欢

热点阅读