iOS手撕protocol型路由技术|青训营笔记

107 阅读4分钟

iOS手撕protocol型路由技术|青训营笔记

这是我参与「第四届青训营 」笔记创作活动的第2天

路由

首先想到的就是路由器,和网络基础的一些知识

所以这里先讲一下可能设计到的一个知识点

协议

从网络基础的角度出发,我们在链接网络或者请求数据的时候,都会设计到协议

比如我们常说的TCP/IP协议等等。

路由路径

每个人都有名字,或者是唯一的身份证号,这用来区别于每个对象

而路径也会有中间节点等等,也就是所谓的层级

路由表

我们的路由器特别聪明,在网络转发的时候,会为每个设备创建路由表

路由返回

有时候,我们需要返回给路由器一定的信息,比如是否路由成功之类的

手撕思路

上面都是简单的概述,现在讲一下细节

protocol协议

/// 被路由的类可以选择回掉的Block,告知路由类相关的信息
typedef void(^RisingRouterResponseBlock)(RisingRouterResponse *response);

#pragma mark - RisingRouterHandler

@protocol RisingRouterHandler <NSObject>

@required

@property (nonatomic, readonly, class) NSArray <NSString *> *routerPath;

+ (void)responseRequest:(RisingRouterRequest *)request completion:(_Nullable RisingRouterResponseBlock)completion;

@end

所有想被路由的类都应该遵守这个协议

所有遵守了这个协议的类都可以被路由

相当于你想得到消息,你就必须遵守某个协议,保证自己能够得到消息

这里,routerPath就是自己的路由名,可以是多个

可以理解如果我呼叫@"xiaoming",你会答应

如果我呼叫@"zhangsan"你也会答应的话,那叫的方式就有两个

而下面那个则是叫你的时候,你应响应的方法

request会在接下来的请求中讲到,completion会在响应种讲到

请求

typedef NS_ENUM(NSUInteger, RisingRouterRequestType) {
    RouterRequestPush,       // 默认,需要push
    RouterRequestController, // 不push,只要VC
    RouterRequestParameters, // 不是VC,只要参数
};

#pragma mark - RisingRouterRequest

@interface RisingRouterRequest : NSObject

#pragma mark Setter

/// 发出请求的Controller,如果为nil,有可能是不需要push
@property (nonatomic, weak, nullable) __kindof UIViewController *requestController;

/// 路由请求所抵达路径
@property (nonatomic, readonly) NSString *responsePath;

/// 请求的情况,默认为RouterRequestPush
@property (nonatomic) RisingRouterRequestType requestType;

#pragma mark Use

/// 使用顶层Controller,如果使用,请确定顶层VC有NavgationController
@property (nonatomic, readonly, nonnull, class) UIViewController *useTopController;

/// 请求发出的所有参数
@property (nonatomic, readonly, nullable) NSDictionary *parameters;

#pragma mark - Method

- (instancetype)init NS_UNAVAILABLE;

+ (instancetype)new NS_UNAVAILABLE;

/// 外链转Request
/// @param url 只要一个URL就够了
+ (instancetype)requestWithURL:(NSURL *)url parameters:(NSDictionary *_Nullable)parameters;

/// 正常的一种Request
/// @param routerPath 路由路径
/// @param paramaters 路由参数
+ (instancetype)requestWithRouterPath:(NSString *)routerPath parameters:(NSDictionary * _Nullable)paramaters;

@end

在oc编程中,路由技术可以简单为三种情况

比如正常要push到一个模块,这样做的好处就是在重构的时候,保证以前的能运行的情况下,抽离出模块

如果类似飞书这种自定义tabbar可以考虑只要vc,还可以将这种vc作为childController显示到已有页面

而作为如果只是需要一个数据,比如我只想要课表数据,那么我可以直接去请求到所需要的东西

当然,在push情况下,请求者是个VC的话,可以直接使用该vc.navagationController去push

路由所抵达的路径应该也是唯一的,不可以同时呼叫多个对象进行响应

如果想实现类似呼叫多个功能,应该在对呼叫路由路径的算法上进行类扩展

传递当然少不了参数,dictionary作为最优雅的方式,里面可以去传递各种对方可能需要的东西

创建这个request对象就非常简单了,可以通过url方式(多用于外链),也可以直接写routerPath

路由

typedef void(^RisingRouterCompletionBlock)(RisingRouterRequest *request, RisingRouterResponse *response);

#pragma mark - RisingRouter

@interface RisingRouter : NSObject

/// 单例Router
@property(nonatomic, readonly, class) RisingRouter *router;

- (instancetype)init NS_UNAVAILABLE;

+ (instancetype)new NS_UNAVAILABLE;

/// 开启路由,使用block进行添加setter
/// @param request 请求路径
/// @param completion 路由完成后回掉
- (void)handleRequest:(RisingRouterRequest *)request
           complition:(_Nullable RisingRouterCompletionBlock)completion;

@end

#pragma mark - NSObject (RisingRouter)

@interface NSObject (RisingRouter)

@property (nonatomic, readonly) RisingRouter *router;

@end

路由类作为最大的类,主要作用就是去路由,也就是上面所看到的唯一的属性方法

发送一个请求,而响应者也许会掉用complition回掉,告知请求者相关信息

响应

#pragma mark - ENUM (RisingRouterResponseError)

typedef NS_ENUM(NSUInteger, RisingRouterResponseError) {
    RouterResponseSuccess,      // 成功响应
    
    RouterResponseParameterLoss,        // 参数缺失
    RouterResponseParameterMatchFaild,  // 匹配失败
    RouterResponseParameterClassError,  // 类型错误
    RouterResponseParameterConflict,    // 参数互斥
    
    RouterResponseWithoutNavagation     // 无栈管理
};

#pragma mark - RisingRouterResponse

@interface RisingRouterResponse : NSObject

/// 响应的类,会自动设置
@property (nonatomic, nullable) Class responseClass;

/// 响应的VC,可用于不push,而是要显示这个VC所使用
@property (nonatomic, nullable) __kindof UIViewController *responseController;

/// 错误代码,默认RouterResponseSuccess
@property (nonatomic) RisingRouterResponseError errorCode;

/// 错误描述
@property (nonatomic, copy) NSString *errorDescription;

/// 响应的资源
@property (nonatomic, nullable) id responseSource;

@end

响应作为可要可不要的参数,建议使用,毕竟debug的时候更为方便

请求的错误信息应该枚举出来,这里只枚举了我在开发中常用的

而响应者可以仅仅是类,所以这里只有响应的类

而如果想传递一个成员,或是其他的请求者可能需要的东西,那么可以在responseSource里面书写


以上就是我手撕路由的时候的思路,源码并不重要,重要的是封装一个东西的思路。