天天看点

iOS-组件化开发:路由设计

一、为什么需要路由

首先我们应该清楚为什么要组件化开发,它将

复杂项目拆分出各个模块

,有助于多人大型团队的协同开发。在这里

各个模块可以看作是一个独立的app

,但随着业务的发展,系统变得越来越复杂,项目内各模块之间耦合严重, 这对代码质量,以及开发效率都会造成很大的影响。

iOS-组件化开发:路由设计

为了解决这个问题,路由这个东西就应运而生了;它主要是做啥呢,其实就相当于

各个模块之间的中转处理

,不用各个模块之间直接调用了。这样才是真正意义上的组件化。

iOS-组件化开发:路由设计

二、路由设计

先上路由设计图

iOS-组件化开发:路由设计

1、路由大量应用于前端页面,通过把一个 URL 与一个页面绑定,需要时通过 URL可以方便的打开相应页面。

// 通过路由URL跳转到AViewController
    AViewController *a = routerFromUrl(@"https://h5.m.cn/AViewController");
    [self.navigationController pushViewController:a animated:true];
           

有些页面需要传递参数,URL 协议支持基本类型参数

BViewController *b = routerFromUrl(@"https://h5.m.cn/BViewController?name=123");
    [self.navigationController pushViewController:b animated:true];
           

复杂类型的参数,可以提供一个额外的字典参数 nativeParams, 将复杂参数放到字典中即可:

2、内部实现:路由文件结构

iOS-组件化开发:路由设计

PQRouter:入口文件,集中处理逻辑

.h
// 简单调用
extern id routerFromUrl(NSString *url) ;
// 带有复杂参数
extern id routerFromUrlWithNativeParams(NSString *url, NSDictionary *nativeParams) ;

@interface PQRouter : NSObject

@end

@interface NSObject(PQRouter) <PQRouterProtocol>

+ (PQRouterResponse *) handleUrlWithRequest:(PQRouterRequest *) request ;

@end

@interface UIViewController(PQRouter) <PQRouterProtocol>

- (instancetype) initWithRequest:(PQRouterRequest *) request ;

@end


           
.m

#import "PQRouter.h"
#import <objc/runtime.h>
#import "PQRouterRequest.h"
@implementation PQRouter

// 解析url
NSDictionary *parseUrl(NSString *url) {
    if (!url || url.length <= 0) {
        return nil ;
    }
    
    NSArray *urls = [url componentsSeparatedByString:@"?"] ;
    NSString *host = [urls firstObject] ;
    NSString *handle = [[host componentsSeparatedByString:@"/"] lastObject] ;

    NSMutableDictionary *params = [NSMutableDictionary dictionary] ;
    
    if (urls.count > 1) {
        NSString *urlParamString = urls.lastObject ;
        NSArray *keyValueArray = [urlParamString componentsSeparatedByString:@"&"] ;
        for (NSString *kvString in keyValueArray) {
            NSArray *kvs = [kvString componentsSeparatedByString:@"="] ;
            if (kvs.count == 2) {
                params[kvs[0]] = [kvs[1] stringByRemovingPercentEncoding] ;
            }
        }
    }
    return @{@"handle": handle,@"host": host, @"params": params} ;
}

// 生成PQRouterRequest请求对象
PQRouterRequest *requestWithUrl(NSString *url, NSDictionary *nativeParams) {
    NSDictionary *hostParamsMapping = parseUrl(url) ;
    
    NSString *handle = hostParamsMapping[@"handle"] ;
    NSString *host = hostParamsMapping[@"host"] ;
    NSMutableDictionary *params = hostParamsMapping[@"params"] ;
    
    PQRouterRequest *request = [[PQRouterRequest alloc] init] ;
    request.host = host ;
    request.params = params ;
    request.nativeParams = nativeParams ;
    request.url = url ;
    request.handler = handle;
    return request ;
}

id routerFromUrl(NSString *url) {
    return routerFromUrlWithNativeParams(url,nil);
}
    
id routerFromUrlWithNativeParams(NSString *url, NSDictionary *nativeParams) {
    PQRouterRequest *request = requestWithUrl(url,nativeParams) ;
    Class c = NSClassFromString(request.handler) ;
    if (c && [c respondsToSelector:NSSelectorFromString(@"handleUrlWithRequest:")]) {
        PQRouterResponse *response = [c handleUrlWithRequest:request] ;
        if (response.toHandler) {
            return response.toHandler;
        }
    }
    
    return [[c alloc] init] ;
}
@end

@implementation NSObject(PQRouter)

+ (PQRouterResponse *) handleUrlWithRequest:(PQRouterRequest *) request {
    PQRouterResponse *response = [[PQRouterResponse alloc] init] ;
    if ([self isKindOfClass:object_getClass(UIViewController.class)]) {
        response.toHandler = [[self alloc] initWithRequest:request] ;
    } else {
        response.toHandler = [[self alloc] init] ;
    }
    
    return response ;
}

@end

static char kRouterRequestKey = '\0' ;
@implementation UIViewController(PQRouter)

- (instancetype) initWithRequest:(PQRouterRequest *) request  {
    self = [self init];
    
    if (self) {
        objc_setAssociatedObject(self, &kRouterRequestKey, request, OBJC_ASSOCIATION_RETAIN_NONATOMIC) ;
    }
    return self ;
}
@end

           

PQRouterRequest:路由统一请求对象

@interface PQRouterRequest : NSObject

@property (nonatomic, copy) NSString *host ;
@property (nonatomic, copy) NSString *url ;
@property (nonatomic, copy) NSString *handler ;
@property (nonatomic, strong) NSDictionary *params ;
@property (nonatomic, strong) NSDictionary *nativeParams ;

@end

           

PQRouterResponse:路由统一返回对象

@interface PQRouterResponse : NSObject
@property (nonatomic, strong) id toHandler ;
@end
           

PQRouterProtocol:模块需要遵循的协议

@protocol PQRouterProtocol <NSObject>
@optional
+ (id) handleUrlWithRequest:(PQRouterRequest *) request ;
@end
           

3、案例

+ (id)handleUrlWithRequest:(PQRouterRequest *)request {
    NSLog(@"%@",request.params);
    BViewController* controller = [[BViewController alloc] init];
    PQRouterResponse *response = [[PQRouterResponse alloc] init] ;
    response.toHandler = controller;
    return response;
}
           
/**
 *  跳转B
 */
- (IBAction)pushB:(id)sender {
    // 通过路由URL跳转到BViewController并带有name参数
    BViewController *b = routerFromUrl(@"https://h5.m.cn/BViewController?name=123");
    [self.navigationController pushViewController:b animated:true];
}
           
// BViewController打印结果,可见参数带过来了
2020-06-07 17:29:42.487097+0800 ModuleProject[4530:175751] {
    name = 123;
}
           

才疏学浅,如有不正确的地方请多多指教。推荐几个比较好的博客供大家学习

iOS应用架构谈 组件化方案

滴滴的组件化实践与优化

京东 iOS 客户端组件管理实践