前言
偶然在网上看到casa的网络层设计方案,在以前的iOS开发中,用的最多的就是集约型的网络请求+去Model的形式,然而在看完这篇文章之后我觉得好像发现了新大陆,原来还可以这么玩。
- 此网络层设计方案最大的好处就是解耦,每个API都有特定的APIManager管理,可以方便的缓存数据以及取消网络请求。
- 在集约型网络基础上封装了散约型的API,参数以及请求以及处理请求数据全部封装到该APIManager方法里面。
- 通过Delegate的方法回调,更好的方便调试以及释放对象。如果以block来调用的话首先不方便调试,其次会延长对象的生命周期,对象需要必须等到网络请求回来之后才能释放。
- 提供一个BaseAPIManager,然后通过IOP的方式约束子类必须要遵循APIRequestProtocol,通过实现该协议的方式来提供给API参数,这样父类就不用提供空载函数。 该父类还提供一个代理,该代理用于返回数据。
- 最后是去Model化的设计,APIRequestProtocol 提供了一个
-(id)fetchDataWithReformer:(id<ReformerProtocol>)reformer;
协议方法,在该方法里面可以会传入一个遵守ReformProtocol协议的reformer,到时reformer就可以自定义处理数据的方法,返回特定的数据- ReformProtocol有一个
- (id)reformDataWith:(LMBaseAPIManager*)apiManager;
的协议方法,里面可以根据apiManager做判断,然后返回不同的数据,也就是说,一个reformer可以对应多个apiManager。
具体的原理和好处在case的文章里面已经说得很清楚了
项目结构:
- LMNetwork是具体的网络出口类,里面提供了集约化的网络请求方法,通过block返回数据。
LMBaseAPIManager是网络请求基类,里面有一个遵循了APIRequestProtocol协议的request,这个协议提供了网络请求参数的的方法和处理网络请求结果的方法。发送网络请求的时候会通过这个request获取必要的参数,以及返回数据的时候通过这个类处理数据。
该类还提供了一个遵循了APIResponseProtocol的代理对象,当网络返回数据的时候会调用该协议方法,该协议方法返回一个遵循APIRequestProtocol的request,调用方需要调用
fetchDataWithReformer:
方法,并且传入一个reformer,然后在该方法的实现里面就可以将apiManager传给reformer,reformer可以根据不同apiManager做不同的数据返回了。LMBaseAPIManager还提供了两个属性,一个responseData和error,网络请求结果和错误都会放到这里面,到时将apiManager传到reformer的时候,通过这两个属性可以取出数据。
LMBaseAPIManager还可以提供一个isCache的方法,可以自己设置缓存策略。
总的来说,LMBaseAPIManager需要派生一个子类,然后子类需要实现apiRequestProtocol方法,提供网络必要的参数。调用
startRequest
方法之后调用LMNetwork发送网络请求。当网络请求回来之后,通过代理回调结果,并且将apiManager返回,调用方收到回调时候,传入reformer对数据进行处理。
LMReformerProtocol是一个协议,里面有一个协议方法
- (id)reformDataWith:(LMBaseAPIManager*)apiManager;
该方法用来处理返回数据。LMProductionAPIManager是LMBaseAPIManagerd的派生类,里面提供了apiRequestProtocol协议方法的实现。
代码
以上已经将框架介绍清楚了,下面是代码实现:
- 首先初始化productionAPIManager并且设计代理。
1 | - (LMProductionAPIManager *)productionAPIManager{ |
由于LMProductionAPIManager是LMBaseAPIManagerd的子类,因此会调用父类的init方法
1 | - (instancetype)init{ |
由于子类遵循了 @interface LMProductionAPIManager: LMBaseAPIManager<APIRequestProtocol>
APIRequestProtocol协议,因为该类内部有一个request的属性指向该对象。而该对象是遵循了上述的协议,所以要实现上述协议里面的方法,接下来看看LMProductionAPIManager里面的实现:
1 | @implementation LMProductionAPIManager |
该对象内部提供了url以及get方法和参数,还提供处理返回数据的实现。
接下来调用
[self.productionAPIManager startRequest];
方法,看看该方法内部会做什么:1
2
3
4
5
6
7
8
9
10
11
12
13- (void)startRequest{
[[LMNetwork sharedInstance]requestMethod:[self.request apiRequestMethod] url:[self.request apiRequestName] parameters:[self.request apiRequestParameters] finishBlock:^(id data, NSError *error) {
if (error) {
self.responseError = error;
}
else{
self.responseData = data;
}
if ([self.response respondsToSelector:@selector(apiResponseSuccess:)]) {
[self.response apiResponseSuccess:self.request];
}
}];
}
该方法会发送网络请求并且将请求后的数据复制给当前对象的相关属性,然后通过代理回到结果,并且将当前请求对象返回。
调用方收到代理之后进行处理
1
2
3
4
5- (void)apiResponseSuccess:(id<APIRequestProtocol>)request{
[MBProgressHUD hideHUD];
self.productionArr = [request fetchDataWithReformer:[LMProductReformer new]];
[self.tableView reloadData];
}收到回调之后首先对初始化一个LMProductionReform对象,该对象遵循了LMReformerProtocol协议,待会再来看里面的实现。
然后调用request的协议方法
fetchDataWithReformer
该方法的实现在第一点已经提到了,该方法内部会看一下有没有传reformer进来,如果没有的话直接返回未经处理的对象,如果有的话那就调用reformer的LMReformerProtocol协议方法。接下来来看
LMReformerProtocol
方法的实现1
2
3
4
5
6
7
8
9
10
11
12
13
14
15- (id)reformDataWith:(LMBaseAPIManager *)apiManager{
//这里根据不同的APIManager返回相应的数据
if ([apiManager isKindOfClass: [LMProductionAPIManager class]]) {
NSDictionary *dict = apiManager.responseData;
NSArray *list = dict[@"list"];
NSMutableArray *mArr = [NSMutableArray arrayWithCapacity:list.count];
for (NSDictionary *dict in list) {
NSDictionary *infoDict = @{kLMProductionVer:dict[@"ver"],kLMProductionVerDesc:dict[@"verDesc"]};
[mArr addObject:infoDict];
}
return mArr;
}else{
return nil;
}
}
reform在这里根据不同的APIManager进行不同的解析,返回不同的数据。
到这里,整个网络和去model的设计就完成了。每个API都应该对应一个APIManager,但是可以使用同一个reformer,然后reformer对apimanager做判断从而返回不同的数据。
值得注意的是:由于我们用字典的形式来存储数据,所以肯定会有hardcode,我们可以定义一个头文件了里面包含所有我们用到的字符串常量,然后在reformer的实现文件里面给这些常量赋值。
如在LMProductReformKeys.h
中
1 | extern NSString *const kLMProductionVer; |
在LMProductReformer.m
中
1 | NSString * const kLMProductionVer = @"productionVer"; |
这样只要引入LMProductReformer.h都可以使用该常量。
完整的demo
以上。