iOS路由设计思路

前言

iOS解耦方案大部分是通过路由,由此出现了各种不同的路由设计。在看过一些路由设计后,笔者对路由引发了一些思考。由此设计出了一个配置简单的路由,demo地址,本文从以下几方面阐述设计思路。

  • URI定义
  • 整体设计
    • URI设计
    • 映射表设计
    • 路由设计

一 URI定义

URI组成如下

scheme://userInformation@host/path?query#fragment
scheme://path

二 整体设计

1 URI设计

后端和前端采用第一种URI定义方式,客户端解耦时候一般采用第二种URI定义方式。一些产品同时有客户端和h5页面,这种情况下如果客户端某一个页面出现问题,可以先跳转到h5的相应页面,直到问题修复后,再跳转回原生页面。网上的很多路由没有考虑到上述情况,只是做了客户端内部的页面跳转。为了解决这个问题demo中的URI采用第一种定义方式,客户端和h5的URI只有scheme不一致,后端只用修改scheme即可跳转不同页面,减少页面映射的工作量。

static NSString *projectScheme = @"route";
static NSString *projectHost = @"127.0.0.1";

2 映射表设计

route.plist,用于path和ViewController的映射,数据结构跟后端接口返回的一致,这样可以通过后端下发路由表。

     
        "data": {  
            "/list": {  
                "vc": "ListViewController",   
                "present": NO       
            },   
            "/detail": {  
                "vc": "DetailViewController",  
                "present": NO  
            },  
            "/present": {  
                "vc": "PresentViewController",  
                "present": YES  
            },  
            "/push": {  
                "vc": "PushViewController",  
                "present": NO  
            }  
        },  
        sign:"0"      

其中
sign:用于校验服务端是否有新的plist文件
data字段中放置的是映射表,key为URL,value为ViewController
data中的vc:url对应的类名
data中的present:页面跳转方式

tips 映射表可以采用差量下发形式,这样避免页面增多后映射表文件过大,即用BSDiff方式。

3 路由设计

整个路由在Core这个文件夹中,流程图如下


iOS路由设计思路_第1张图片
image.png

ROURouter:根据URL返回ViewController

- (UIViewController *)openURL:(NSURL *)url {
    //检测url是否合法
    if (![self checkURLParameter:url]) {
        return nil;
    }
    
    //检测自定义的url是否符合配置
    if (![self checkCustomURLParameter:url]) {
        return nil;
    }
    
    Class class = NSClassFromString([self getVCNameFromURLPath:url.path]);
    if (class) {
        id obj = [[class alloc] init];
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wundeclared-selector"
        if ([class instancesRespondToSelector:@selector(setUrl:)]) {
            [obj performSelector:@selector(setUrl:) withObject:url];
        }
#pragma clang diagnostic pop
        if ([obj isKindOfClass:[UIViewController class]]) {
            return obj;
        } else {
            return nil;
        }
    } else {
        return nil;
    }
}

ROUEntery:如果scheme的开头是http(s)等,就直接跳转web页面;如果是自定义的scheme,就根据ROURouter返回的ViewController跳转到相应页面,如果找不到页面就跳转到空页面。

- (void)openURL:(NSURL *)url {
    //url是否可以跳转web页面
    if ([self canOpenWebViewWithScheme:url.scheme]) {
        self.webViewController.url = url;
        [[ROUNavigator manager].rootViewController pushViewController:self.webViewController animated:YES];
    } else {
        UIViewController *vc = [[ROURouter manager] openURL:url];
        if (!vc) {
            [[ROUNavigator manager].rootViewController pushViewController:self.blankViewController animated:YES];
        } else {
            if ([self presentToNextVCFromURLPath:url.path]) {
                [[ROUNavigator manager].rootViewController presentViewController:vc animated:YES completion:nil];
            } else {
                [[ROUNavigator manager].rootViewController pushViewController:vc animated:YES];
            }
        }
    }
}

如果ViewController需要接收参数在头文件中定义

@property (nonatomic, strong) NSURL *url;

url中query即为传递的参数

tips 已经内置了NSURL+Addition用来解析query,解析好的query中value已经进行了UTF8解码


补充

增加回掉

由于之前的路由没有回调不利于页面间传值,用起来不是很方便,最近加了回调,ViewController中增加了responseBlock,虽然返回值定义的是id,但是根据网络传输规范此处最好用json进行数据传输。

@property (nonatomic, copy) void (^resopnseBlock)(id response);

使用方法如下:

 [[ROUEntery manager] openURL:url resopnseBlock:^(id response){
    }];

这种方法只能在页面间进行数据传输,如果从别的app打开这个app则用[[ROUEntery manager] openURL:url];这个方法打开指定页面,或者使用[[ROUEntery manager] openURL:[NSURL URLWithString:url resopnseBlock:nil];打开指定页面也可。

个人觉得后者会对调用者造成困扰,没用相应的回掉事件还需要传nil,所以建议没用回调时候使用[[ROUEntery manager] openURL:url];

增加是否打开页面的回调

设计之初考虑到页面打不开情况,所以页面打不开就会跳转到一个空页面,这些跳转使用方是没用感知的,后来觉得应该给使用方是否打开页面的返回值,改动如下:

/**
 跳转到指定的vc

 @param url 入口地址
 @return 是否成功打开页面
 */
- (BOOL)openURL:(NSURL *)url;

/**
 跳转到指定vc
 如果vc跟上一级页面有数据传递用该方法
 
 @param url 入口地址
 @param response 回调,不指定数据格式,建议用json进行数据传递
 @return 是否成功打开页面
 */
- (BOOL)openURL:(NSURL *)url resopnseBlock:(id)response;

demo保留打开不成功跳转空页面的逻辑,读者根据实际业务进行调整。

感谢

感谢koa这个node框架给了该路由设计的灵感

参考资料

http://koajs.com
https://en.wikipedia.org/wiki/Uniform_Resource_Identifier
https://halfrost.com/ios_router/

你可能感兴趣的:(iOS路由设计思路)