iOS网络层设计-Client 实现
iOS 网络层设计
iOS 网络层 Client 的功能
要设计网络层,首先要确定网络层需要实现哪些功能:
- 接口请求(普通的
get
、post
请求、文件上传请求等); - 结果初步解析(将返回的
xml
、json
格式数据解析成可识别的字典 ) - 取消请求(
[task cancle]
)
正常的 Client 都会有这三种需求。高级一点的会有缓存之类的设置,这些功能都是属于这三个基础功能之外的额外设置,这里的话不讨论。
功能实现
基于的 AFN 举的例子,不过用 NSURLSession
也一样。
1、接口请求功能:
作为一个基本请求功能,首先需要的是:
- 请求的 url 地址
- 使用的参数
- 成功/失败的回调
以 POST 为例:
- (NSURLSessionDataTask *)postToMethod:(NSString*)method WithDict:(NSDictionary*)dict delegate:(id)delegate {
AFHTTPSessionManager *manager = [AFHTTPSessionManager manager];
NSURLSessionDataTask *task = [manager POST:MAINURL parameters:dict progress:nil success:^(NSURLSessionDataTask * _Nonnull task, id _Nullable responseObject) {
// 成功请求到数据
} failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {
// 请求失败
}];
[task resume];
return task;
}
在这个方法中,我使用协议来实现处理失败和成功的请求。
在定义协议时,我要求实现两个方法,一种是必须实现的成功会掉,一种是可选的失败回调:
@protocol NetClientDelegate <NSObject>
@required
/**
请求成功
@param successDict 请求结果
*/
- (void)netClientSuccess:(NSDictionary *)successDict;
@optional
/**
请求失败
@param failDict 失败原因
*/
- (void)netClientFail:(NSDictionary *)failDict;
@end
因为无论什么情况下,你都会对请求成功后的结果进行处理,否则你将不知道什么时候进行下一个动作;而失败的话则有可能在该类中就直接处理了,无需后面的操作(例如我就习惯在失败的时候直接在网络层类提示请求失败并显示失败原因,虽然从耦合角度上来说并不好,但是确更直观的展现失败原因)。
2、结果解析
在成功得到请求的返回值之后,首先需要处理的是格式化的问题:
如果你返回的是 xml
或者 data
或者 json
数据,那么首先需要进行转换成能接受的 NSDictionary
格式数据 ;
然后再通过判断字典中的错误码来判断这是一个有效的请求还是无效的请求;(错误码跟需要后台协商)
并根据请求是否有效执行不同的回调,在回调方法中执行具体的解析并返还给上一层。
3、取消请求
在请求过程中,通常会有取消请求,有时候可能是误操作,也有可能是太慢了想重新请求等,不管什么原因,会使用到取消。不过取消的话,有一点要注意:发送的请求就算取消了,也有可能发送到服务器上。
因为取消只是取消接受,你已经发送出去的请求是无法控制的。
取消请求的实现:
- 保存 AFN 调用返回的 task 任务;
- 将 task 放入一个 task 数组当中,为之后停止网络做准备;
- 将保存的任务返回给调用层,使调用层知道现在 task 是哪个,允许调用层通过 task 取消该请求。
demo
//NetClient.h
#import <Foundation/Foundation.h>
#define MAINURL @"http://xxxxxx.xxx"
@protocol NetClientDelegate <NSObject>
@required
/**
请求成功
@param successDict 请求结果
*/
- (void)netClientSuccess:(NSDictionary *)successDict;
@optional
/**
请求失败
@param failDict 失败原因
*/
- (void)netClientFail:(NSDictionary *)failDict;
@end
@interface NetClient : NSObject
@property (nonatomic, strong) id<NetClientDelegate> delegate;
#pragma mark- method
/**
单例
@return 返回单例
*/
+ (NetClient *)shareNetClient;
/**
取消所有任务
*/
+ (void)cancleAllTasks;
/**
取消单个任务
@param task 任务
*/
+ (void)cancleTask:(NSURLSessionDataTask *)task;
/**
取消所有任务
*/
- (void)cancleAllTasks;
/**
取消单个任务
@param task 任务
*/
- (void)cancleTask:(NSURLSessionDataTask *)task;
/**
使用AFNetworkingPost数据
@param method 方法名
@param dict 非固定参数
@param delegate 委托实现
*/
- (NSURLSessionDataTask *)postToMethod:(NSString*)method WithDict:(NSDictionary*)dict delegate:(id)delegate;
@end
// NetHelper.m
#import "NetClient.h"
#import "AFNetWorking.h"
#import <AVFoundation/AVFoundation.h>
@interface NetClient()
@property (nonatomic, strong) NSMutableArray<NSURLSessionDataTask *> *taskArray;
@end
@implementation NetClient
static NetClient *__shareClient;
#pragma mark- public method
+ (NetClient *)shareNetClient {
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
__shareClient = [[super allocWithZone:NULL] init];
});
return __shareClient;
}
+ (void)cancleAllTasks {
[[NetClient shareNetClient] cancleAllTasks];
}
+ (void)cancleTask:(NSURLSessionDataTask *)task {
[[NetClient shareNetClient] cancleTask:task];
}
- (void)cancleAllTasks {
[self.taskArray enumerateObjectsUsingBlock:^(NSURLSessionDataTask * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
[obj cancel];
}];
[self.taskArray removeAllObjects];
}
- (void)cancleTask:(NSURLSessionDataTask *)task {
[task cancel];
[self.taskArray removeObject:task];
}
/**
使用AFNetworkingPost数据
@param method 方法名
@param dict 非固定参数
@param delegate 委托实现
*/
- (NSURLSessionDataTask *)postToMethod:(NSString*)method WithDict:(NSDictionary*)dict delegate:(id)delegate {
AFHTTPSessionManager *manager = [AFHTTPSessionManager manager];
/* Https 验证
AFSecurityPolicy *securityPolicy = [AFSecurityPolicy policyWithPinningMode:AFSSLPinningModeCertificate];
securityPolicy.validatesDomainName = NO;
manager.securityPolicy = securityPolicy;
*/
NSURLSessionDataTask *task = [manager POST:MAINURL parameters:dict progress:nil success:^(NSURLSessionDataTask * _Nonnull task, id _Nullable responseObject) {
[self.taskArray removeObject:task];
if (/* DISABLES CODE */ (YES)) {
if ([delegate respondsToSelector:@selector(netClientSuccess:WithTask:)]) {
[delegate netClientSuccess:responseObject WithTask:(NSURLSessionDataTask *)task];
}
}
else{
if ([delegate respondsToSelector:@selector(netClientFail:WithTask:)]) {
[delegate netClientFail:responseObject WithTask:(NSURLSessionDataTask *)task];
}
}
} failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {
[self.taskArray removeObject:task];
if ([delegate respondsToSelector:@selector(netClientFail:WithTask:)]) {
[delegate netClientFail:nil WithTask:(NSURLSessionDataTask *)task];
}
}];
[task resume];
return task;
}
+ (instancetype)allocWithZone:(struct _NSZone *)zone {
return [NetClient shareNetClient];
}
- (id)copyWithZone:(NSZone *)zone{
return [NetClient shareNetClient];
}
+ (id)copyWithZone:(struct _NSZone *)zone{
return [NetClient shareNetClient];
}
+ (id)mutableCopyWithZone:(struct _NSZone *)zone{
return [NetClient shareNetClient];
}
- (id)mutableCopyWithZone:(NSZone *)zone{
return [NetClient shareNetClient];
}
#pragma mark - getter/setter
- (NSMutableArray *)taskArray {
if (!_taskArray) {
_taskArray = [NSMutableArray array];
}
return _taskArray;
}
@end
在网络层中,我将Client设置为单例,主要是为了可以管理全部的任务。保证每一个任务的生成都在唯一的数组当中。
当任务结束请求之后,需要把当前的任务从数组中移除,保证数组中的都是正在请求的任务。
暴露出来发送请求的方法,取消单个与取消所有的方法。发送请求是因为 delegate 的原因,所以只能是对象方法。其余的则对象方法与类方法都适用。在这里我只写了普通的 Post 请求,get 与文件请求方法类似,只是调用的时候内容有所不同而已。
至此,Client 已经完成。