Arclin

Advocate Technology. Enjoy Technology.

0%

网络层方法封装3

网络层方法封装3.1

** 其实很久之前就想写写这个东西,不过因为项目的原因一直没时间去整理出来,所以决定还是分开几天去写完这个东西吧

  • 新增特性:

    • 3.0
      • 多缓存策略
      • 用宏定义减少代码量
      • 使用 plist统一错误信息设置
    • 3.1
      • 输出一个漂亮的Log
  • 先讲讲本地持久化策略的选择

    • 数据量小,单一的时候(比如一个模型数组,数组不大而且模型的成员属性不是很多),可以使用 NSKeyArchive 归档

    • NSUserDefault 用于储存用户首选项

    • KeyChain储存一些密码之类的东西

    • 数据库储存数据量比较大,字段比较多的模型数组

  • 缓存策略

1
2
3
4
5
6
7
8
typedef NS_ENUM(NSInteger,DKCacheStrategy){
DKCacheStrategy_CACHE_ONLY, // 只从本地取数据
DKCacheStrategy_NETWORK_ONLY, // 只从网络取数据(不缓存)
DKCacheStrategy_NETWORK_AND_CACHE, // 从网络取数据后缓存(缓存结束不回调)
DKCacheStrategy_CACHE_ELSE_NETWORK, // 先取缓存,如果没有数据的话,才从网络取数据
DKCacheStrategy_CACHE_THEN_NETWORK // 先取缓存,再加载网络数据,网络数据加载完会更新缓存,这个选择会有两次回调
DKCacheStrategy_AUTOMATIC // 根据当前网络环境自动选择,如果有网络(WIFI/WLAN)就取网络数据,没网络就取缓存数据
};
  • Demo 说明
    • github地址:没有
    • 下载地址: NetworkDemo
    • 选择本地持久化方式: YYCache,如果有更适合的缓存机制,那改一改也挺简单
  • 方法接口设计
1
2
3
4
5
6
7
8
9
10
11
12
/**
* 发送HTTP请求
*
* @param method 请求方法,你可以写@"get"或者@"post"(不区分大小写)或者已经定义好的 kGET 或 kPOST
* @param strategy 缓存策略
* @param header 请求头,可为空
* @param params 请求参数,可为空
* @param block 返回回调,这个就不要空啦
*
* @return 请求标示 id 可以用来取消请求
*/
- (NSInteger)requestForMethod:(NSString *)method cacheStragety:(DKCacheStrategy)strategy url:(NSString *)URLString header:(NSDictionary *)header params:(NSDictionary *)params responseBlock:(DKHTTPResponseBlock)block;

之前两个方法继续保留,在这里就不说明了

1
2
- (void)cancelAllRequest;
- (void)cancelRequestWithRequestIds:(NSArray *)requestIds;

然后写两个内部方法,分别是取缓存和取网络数据

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
#pragma mark - 仅本地
- (NSInteger)requestWithCacheOnlyStrategyWithUrl:(NSString *)URLString params:(NSDictionary *)params requestBlock:(DKHTTPResponseBlock)block
{
DKResponse *response = (DKResponse *)[self.cache objectForKey:[self cacheKeyForRequestUrl:URLString params:params]];
if (block) {
if (response) {
block(response);
}else{
block(KERROR_RESPONSE(-1));
}
}
return 0;
}

#pragma mark - 仅网络
- (NSInteger)requestWithNetworkOnlyStrategyForMethod:(NSString *)method url:(NSString *)URLString header:(NSDictionary *)header params:(NSDictionary *)params responseBlock:(DKHTTPResponseBlock)block
{
NSNumber *taskIdentifier = 0;
if ([method.uppercaseString isEqualToString:@"GET"]) {
DKCALLAPI(GET, taskIdentifier);
}else if([method.uppercaseString isEqualToString:@"POST"]){
DKCALLAPI(POST, taskIdentifier);
}else{
return 0;
}
return taskIdentifier.integerValue;
}

这里的宏DKCALLAPI 定义 主要是为了不想写 POST和 GET两个方法,在这里写一次就够了

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
#define DKCALLAPI(REQUEST_METHOD,REQUEST_ID) \
{\
AFHTTPSessionManager *mgr = [AFHTTPSessionManager manager];\
AFHTTPRequestSerializer *requestSerializer = mgr.requestSerializer;\
if (header) {\
[header enumerateKeysAndObjectsUsingBlock:^(id _Nonnull key, id obj, BOOL * _Nonnull stop) {\
[requestSerializer setValue:obj forHTTPHeaderField:key];\
}];\
}\
requestSerializer.timeoutInterval = kTimeOutInterval;\
NSURLSessionTask *task = [mgr REQUEST_METHOD:URLString parameters:params progress:nil success:^(NSURLSessionDataTask * _Nonnull task, id _Nullable responseObject) {\
DKResponse *resp = [DKResponse mj_objectWithKeyValues:responseObject];\
resp.rawData = responseObject;\
resp.taskIdentifier = task.taskIdentifier;\
if(block){\
block(resp);\
}\
} failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {\
DKResponse *resp = [DKResponse responseWithErrorOnly:error.description code:error.code];\
if (block) {\
block(resp);\
}\
}];\
REQUEST_ID = @(task.taskIdentifier);\
[self.dispatchTable setObject:task forKey:REQUEST_ID];\
}

然后 就可以实现我们的接口了 switch 里面的东西就就根据注释写吧

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
- (NSInteger)requestForMethod:(NSString *)method cacheStragety:(DKCacheStrategy)strategy url:(NSString *)URLString header:(NSDictionary *)header params:(NSDictionary *)params responseBlock:(DKHTTPResponseBlock)block
{
__weak typeof(self) weakSelf = self;
NSInteger requestId;
switch (strategy) {
case DKCacheStrategy_CACHE_ONLY:
// 调用获取缓存方法
break;
case DKCacheStrategy_NETWORK_ONLY:
// 调用获取网络方法
break;
case DKCacheStrategy_NETWORK_AND_CACHE:
// 调用网络方法,回调后写入本地缓存
break;
case DKCacheStrategy_CACHE_ELSE_NETWORK:
// 调用本地缓存方法,在回调中判断是否有缓存,如果没有的话调用网络方法
break;
case DKCacheStrategy_CACHE_THEN_NETWORK:
// 调用本地方法接着调用网络方法
break;
case DKCacheStrategy_AUTOMATICALLY:
// 调用网络方法,回调中如果返回错误则调用本地缓存方法
break;
default:
break;
}
return requestId;
}

统一错误表处理
-建立一个plist 文件

1
2
3
4
5
6
7
8
9
10
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>-2</key>
<string>网络连接失败</string>
<key>-1</key>
<string>没有缓存</string>
</dict>
</plist>

一个成员属性加一个方法去调用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
- (NSDictionary *)errorDic
{
if (!_errorDic) {
NSString *path = [[NSBundle mainBundle] pathForResource:@"DKError" ofType:@"plist"];
_errorDic = [NSDictionary dictionaryWithContentsOfFile:path];
}
return _errorDic;
}

- (NSString *)errorDescriptionWithCode:(NSInteger)code
{
NSString *codeStr = [NSString stringWithFormat:@"%zd",code];
if ([self.errorDic.allKeys containsObject:codeStr]) {
return self.errorDic[codeStr];
}else{
NSLog(@"没有定义该类型错误");
return nil;
}
}

包装成宏方便调用

1
#define KERROR_RESPONSE(errCode) [DKResponse responseWithErrorOnly:[self errorDescriptionWithCode:errCode] code:errCode]

3.1 输出一个漂亮的 Log

1
2
3
4
5
6
7
8
9
10
/**
* 打印一个漂亮的 log
*
* @param method 请求方法
* @param url 接口地址
* @param params 参数
* @param response 响应对象
* @param showRequestContent 是否显示响应数据
*/
+ (void)logDebugInfoWithMethod:(NSString *)method URL:(NSString *)url params:(NSDictionary *)params response:(DKResponse *)response showRequestContent:(BOOL)showRequestContent;

里面主要是字符串拼接.要看详细的话就去下载 Demo吧!