做ios项目已经几年了,最初是借用别人的框架,然后再框架上去修改。后来自己项目开始自己学着去写框架结构,不管是用什么样的框架,里面网络层的设计都是比较固定的,所以针对网络层的部分,我单独把这个地方进行了重构,整理成了现在的模样。现在把我的思路整理一下,希望大家如果有什么好的意见或者建议,可以提出来。共同进步。
1、整体思路
整体的思路也是沿用MVC的结构。将两部分内容进行的独立的封装:第一个是网络数据请求,第二个是对网络请求的调用和响应处理部分。而其中的C层,也就是controller部分,只需要处理最后响应回来的数据,进行View的操作即可。
同时因为在项目中考虑到了现在3种数据类型,定义了XML、JSON和ProtocolBuffer。
思路图
上面的图比较简单,只是为了表达一个思路,以及过程。
2、网络请求:NetworkRequest
这部分用的实现是AFNetworking,定义了一个单例的client,封装了client对于网络请求的调用。
先定义一个配置文件NetworkConfig和一个数据封装类NetManager。
// NetworkConfig.h
typedef NS_ENUM(NSInteger, NetErrorCode) {
NetErrorCodeEncryption = 1,//加密错误
NetErrorCodeDecryption = 2,//解密错误
NetErrorCode404 = 404,
NetErrorCode500 = 500,
NetErrorCodeTimeOut = 3,
NetErrorCodeDictionaryModel = 4,//字典数据转换为model数据时错误
NetErrorCodeOther = 5
};
typedef NS_ENUM(NSInteger, NetType) {
XML = 0,
JSON = 1,
PROTOBUF = 2,
};
typedef void (^ReturnValueBlock) (id returnValue);
typedef void (^ErrorCodeBlock) (NetErrorCode errorCode);
NetworkConfig.h文件中,定义了网络异常的类型、网络请求协议的类型,以及网络请求中的block块。在网络请求部分,我是用了block来处理网络请求的响应和异常返回的。
//NetManager.h
@interface NetManager : NSObject
/**
* 原始数据
* json和xml的返回类型为NSDictionary
* protocolBuffer则是NSData类型
*/
@property (nonatomic,strong) id rawData;
@property (nonatomic,assign) NetType type;//返回的数据类型
@end
NetManager的.m文件没有做具体的实现。这个类只是封装了网络请求的响应数据以及数据类型。这里的数据都是原始数据,方便后面中间层对数据的解析处理。
准备工作已经做好了,那么下面就是创建具体的网络请求部分了。先是单例模式的请求client。
//NetHttpRequestClient.h
@interface NetHttpRequestClient : AFHTTPSessionManager
+ (instancetype)sharedClient;
@end
//NetHttpRequestClient.m
@implementation NetHttpRequestClient
+ (instancetype)sharedClient {
static NetHttpRequestClient *_sharedClient = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
_sharedClient = [NetHttpRequestClient manager];
AFSecurityPolicy *securityPolicy = [AFSecurityPolicy policyWithPinningMode:AFSSLPinningModeNone];
[securityPolicy setValidatesDomainName:NO];
_sharedClient.securityPolicy = securityPolicy;
_sharedClient.responseSerializer = [AFHTTPResponseSerializer serializer];
_sharedClient.requestSerializer.timeoutInterval = 40.0f;
});
return _sharedClient;
}
@end
client创建好了,就是网络请求的实现部分了。
#define SERVER_TYPE_XML @"xml/"
#define SERVER_TYPE_JSON @"json/"
#define SERVER_TYPE_PROTOBUF @"protobuf/"
@interface NetRequestClass : NSObject
#pragma 监测网络的可链接性
+ (BOOL) netWorkReachabilityWithURLString:(NSString *) strUrl;
#pragma POST请求
/**
*requestURLString:请求地址,不是全地址,由security+类型+方法名组合而成
*parameter:参数,参数中需要包含方法名的参数
*/
+ (NSURLSessionDataTask *) NetRequestPOSTWithRequestMethod: (NSString *) methodName
WithParameter: (NSDictionary *) parameter
WithRequestType:(NetType)type
WithReturnValeuBlock: (ReturnValueBlock) block
WithErrorCodeBlock: (ErrorCodeBlock) errorBlock;
#pragma protocol_buffer 的请求
/**
* protocol_buffer 的请求
*
* @param methodName 方法名
* @param data protocol_buffer请求的requestData数据
*
*/
+ (NSURLSessionDataTask *) NetProtocolBufferRequestPOSTWithRequestMethod: (NSString *) methodName
WithData: (NSData *) data
WithReturnValeuBlock: (ReturnValueBlock) block
WithErrorCodeBlock: (ErrorCodeBlock) errorBlock;
+(NSMutableDictionary *)addBasicParamters:(NSDictionary *)paramters;
@end
.h文件中,定义了三种类型,同时,将xml/json和protocolBuffer的请求分离开,因为在里面具体的实现部分有一点细微区别,所以没有将三者和在一起处理,而是分离开,方便后期维护。
//NetRequestClass.m
@implementation NetRequestClass
#pragma 监测网络的可链接性
+ (BOOL) netWorkReachabilityWithURLString:(NSString *) strUrl{
__block BOOL netState = NO;
AFHTTPSessionManager *manager = [AFHTTPSessionManager manager];
NSOperationQueue *operationQueue = manager.operationQueue;
[manager.reachabilityManager setReachabilityStatusChangeBlock:^(AFNetworkReachabilityStatus status) {
switch (status) {
case AFNetworkReachabilityStatusReachableViaWWAN:
case AFNetworkReachabilityStatusReachableViaWiFi:
[operationQueue setSuspended:NO];
netState = YES;
break;
case AFNetworkReachabilityStatusNotReachable:
netState = NO;
default:
[operationQueue setSuspended:YES];
break;
}
}];
[manager.reachabilityManager startMonitoring];
return netState;
}
#pragma --mark POST请求方式
+(NSURLSessionDataTask *)NetRequestPOSTWithRequestMethod:(NSString *) methodName
WithParameter:(NSDictionary *) parameter
WithRequestType:(NetType)type
WithReturnValeuBlock:(ReturnValueBlock) block
WithErrorCodeBlock:(ErrorCodeBlock) errorBlock{
NetHttpRequestClient *client = [NetHttpRequestClient sharedClient];
client.requestSerializer = [AFHTTPRequestSerializer serializer];
NSError *theError = nil;
NSString *requestContent = nil;
NSDictionary *allParameters = [self addBasicParamters:parameter];
[allParameters setValue:methodName forKey:@"name"];
switch (type) {
case XML:{//将参数数据转换为xml字符串
NSDictionary *requestDic = [NSDictionary dictionaryWithObject:[self formateXMLParameters:allParameters] forKey:@"methodRequest"];
requestContent = [requestDic innerXML];
}
break;
case JSON:{//将参数数据转换为jsonData
NSData *jsonData = [NSJSONSerialization dataWithJSONObject:allParameters options:NSJSONWritingPrettyPrinted error:&theError];
requestContent = [[NSString alloc] initWithData:jsonData encoding:NSUTF8StringEncoding];
}
break;
default:
break;
}
NSString *encryptXml = @"加密后的字符串";
NSString *serverIpUrl = @"服务器地址";
NSDictionary *encryDic = [[NSDictionary alloc]initWithObjects:@[[NSNull null]] forKeys:@[encryptXml]];
NSString *requestUrl = [NSString stringWithFormat:@"%@%@%@",serverIpUrl,type == JSON?SERVER_TYPE_JSON:SERVER_TYPE_XML,methodName];
//启动请求
NSURLSessionDataTask *dataTask = [client POST:requestUrl parameters:encryDic progress:nil success:^(NSURLSessionDataTask *dataTask, id responseObject) {
if(dataTask){
[dataTask cancel];
dataTask = nil;
}
//获取得到的数据
NSString *theResponse = [[NSString alloc] initWithBytes:[responseObject bytes] length:[responseObject length] encoding:NSUTF8StringEncoding];
NSString *decryptStr = nil;
if(theResponse){
decryptStr = @"解密响应数据";
}
NetManager *manager = [[NetManager alloc]init];
NSDictionary *dataDoc = nil;
if(type == XML){
dataDoc = [NSDictionary dictionaryWithXMLString:decryptStr];
manager.type = XML;
}else if(type == JSON){
dataDoc = [decryptStr objectFromJSONStringWithParseOptions:JKParseOptionLooseUnicode];
manager.type = JSON;
}
manager.rawData = dataDoc;
block(manager);
} failure:^(NSURLSessionDataTask *dataTask, NSError *error) {
if(dataTask){
[dataTask cancel];
dataTask = nil;
}
NSInteger code = error.code;
if(code == NSURLErrorCancelled){//取消
return;
}else if(code == NSURLErrorTimedOut){//超时
errorBlock(NetErrorCodeTimeOut);
return;
}
NSDictionary *userInfo = error.userInfo;//NSURLErrorCancelled
NSHTTPURLResponse *response = userInfo[AFNetworkingOperationFailingURLResponseErrorKey];
if(response.statusCode == 404){
errorBlock(NetErrorCode404);
}else if(response.statusCode > 500){
errorBlock(NetErrorCode500);
}else{
errorBlock(NetErrorCodeOther);
}
}];
return dataTask;
}
+(NSMutableDictionary *)formateXMLParameters:(NSDictionary *)paramters{
NSMutableDictionary *params = [NSMutableDictionary dictionaryWithDictionary:paramters];
for(NSString *key in paramters.allKeys){
NSObject *value = paramters[key];
[params removeObjectForKey:key];
if([value isKindOfClass:[NSString class]]){
[params setObject:value forKey:[NSString stringWithFormat:@"_%@",key]];
}else if([value isKindOfClass:[NSDictionary class]]){
NSDictionary *cp = [self formateXMLParameters:(NSDictionary *)value];
[params setObject:cp forKey:[NSString stringWithFormat:@"%@",key]];
}else if([value isKindOfClass:[NSArray class]]){
NSMutableArray *array = [NSMutableArray new];
for(NSDictionary *dic in (NSArray *)value){
NSDictionary *cp = [self formateXMLParameters:dic];
[array addObject:cp];
}
[params setObject:array forKey:[NSString stringWithFormat:@"%@",key]];
}
}
return params;
}
+(NSMutableDictionary *)addBasicParamters:(NSDictionary *)paramters{
NSMutableDictionary *params = nil;
if(paramters){
params = [[NSMutableDictionary alloc] initWithDictionary:paramters];
}else{
params = [[NSMutableDictionary alloc] init];
}
/***
为请求添加一些默认的基本信息,比如版本号,用户id等等。
**/
return params;
}
+(NSURLSessionDataTask *)NetProtocolBufferRequestPOSTWithRequestMethod:(NSString *)methodName WithData:(NSData *)data WithReturnValeuBlock:(ReturnValueBlock)block WithErrorCodeBlock:(ErrorCodeBlock)errorBlock{
NetHttpRequestClient *client = [NetHttpRequestClient sharedClient];
client.requestSerializer = [AFProtoBufRequestSerializer serializer];
NSDictionary *encryDic = [[NSDictionary alloc]initWithObjects:@[[NSNull null]] forKeys:@[data]];
NSString *serverIpUrl = @"服务器地址";
NSString *requestUrl = [NSString stringWithFormat:@"%@%@%@",serverIpUrl,SERVER_TYPE_PROTOBUF,methodName];
//启动请求
NSURLSessionDataTask *dataTask = [client POST:requestUrl parameters:encryDic progress:nil success:^(NSURLSessionDataTask *dataTask, id responseObject) {
if(dataTask){
[dataTask cancel];
dataTask = nil;
}
//获取得到的数据
NetManager *manager = [[NetManager alloc]init];
manager.rawData = responseObject;
manager.type = PROTOBUF;
block(manager);
} failure:^(NSURLSessionDataTask *dataTask, NSError *error) {
if(dataTask){
[dataTask cancel];
dataTask = nil;
}
NSInteger code = error.code;
if(code == NSURLErrorCancelled){//取消
return;
}else if(code == NSURLErrorTimedOut){//超时
if(errorBlock){
errorBlock(NetErrorCodeTimeOut);
}
return;
}
NSDictionary *userInfo = error.userInfo;//NSURLErrorCancelled
NSHTTPURLResponse *response = userInfo[AFNetworkingOperationFailingURLResponseErrorKey];
if(errorBlock){
if(response.statusCode == 404){
errorBlock(NetErrorCode404);
}else if(response.statusCode > 500){
errorBlock(NetErrorCode500);
}else{
errorBlock(NetErrorCodeOther);
}
}
}];
return dataTask;
}
上面的代码已经很完整了,就不用过多的解释。这是大家能够看到NetManager的作用了,在block的传递中,用的NetManager数据。
3、中间层NetworkModel
这层的主要作用是衔接ViewController与NetworkRequest两层,减少两层的耦合,封装处理网络请求返回的数据。
我们先定义了一个基类和一个协议。基类的作用是让所有实现网络数据处理的类都继承予它,而协议是为了衔接ViewController和NetworkModel之间,为网络数据的传递。
@protocol NetworkModelDelegate ;
@interface NetworkModelClass : NSObject
@property(nonatomic, weak, nullable) id delegate;
@end
@protocol NetworkModelDelegate
@optional
//用于xml或者json数据返回
- (void)networkModelWithNetResponse:(NSDictionary * _Nullable)response withMethodName:(NSString *)methodName;
- (void)networkModelWithNetError:(NetErrorCode)errorCode withMethodName:(NSString *)methodName;
//用于Protobuffer数据
- (void)networkModelWithNetDataResponse:(NSData * _Nullable)response withMethodName:(NSString *)methodName;
@end
这个类很简单,不需要过多的解释。
4、例子
好了,接下来举一个简单的使用例子,就可以明白是怎么工作的了。
创建了一个TestNetworkModel,其继承了NetworkModelClass,在里面,我们创建了两个对外的方法,为了方便,特地提供了一个关于protocolbuffer的使用。
//TestNetworkModel.h
@interface TestNetworkModel : NetworkModelClass
-(void)queryDataWithXML:(NSString *)para;
-(void)queryDataWithProtocolBuffer:(NSString *)para;
@end
//TestNetworkModel.m
@implementation TestNetworkModel{
NSURLSessionDataTask *_task1;
NSURLSessionDataTask *_task2;
}
-(void)queryDataWithXML:(NSString *)para{
if(_task1 && (_task1.state == NSURLSessionTaskStateRunning || _task1.state == NSURLSessionTaskStateSuspended)){
[_task1 cancel];
_task1 = nil;
}
NSDictionary *parameter = nil;
if(para){
parameter = [NSDictionary dictionaryWithObjectsAndKeys:para,@"key_para",nil];
}
__block typeof(self) weakSelf = self;
_task1 = [NetRequestClass NetRequestPOSTWithRequestMethod:@"方法名" WithParameter:parameter WithRequestType:XML WithReturnValeuBlock:^(id netManger){
[weakSelf anlyzeDataWithXML:((NetManager *)netManger).rawData];
} WithErrorCodeBlock:^(NetErrorCode errorCode){
[weakSelf networkError:@"方法名" withErrorCode:errorCode];
}];
}
-(void)anlyzeDataWithXML:(NSDictionary *)data{
/**
此处省略数据解析过程
*/
NSMutableDictionary *dataDic = nil;
NSString *methodName = nil;
if(self.delegate){
[self.delegate networkModelWithNetResponse:dataDic withMethodName:methodName];
}
}
-(void)queryDataWithProtocolBuffer:(NSString *)para{
if(_task2 && (_task2.state == NSURLSessionTaskStateRunning || _task2.state == NSURLSessionTaskStateSuspended)){
[_task2 cancel];
_task2 = nil;
}
NSData *data = nil;//封装的protocolbuffer的请求数据
__block typeof(self) weakSelf = self;
_task2 = [NetRequestClass NetProtocolBufferRequestPOSTWithRequestMethod:@"方法名" WithData:data WithReturnValeuBlock:^(id resp){
if (resp && [resp isKindOfClass:[NetManager class]]) {
NetManager *respData = (NetManager *)resp;
if(weakSelf.delegate && [weakSelf.delegate respondsToSelector:@selector(networkModelWithNetDataResponse:withMethodName:)]){
[weakSelf.delegate networkModelWithNetDataResponse:respData.rawData withMethodName:@"方法名"];
}
}
} WithErrorCodeBlock:^(NetErrorCode errorCode){
[weakSelf networkError:@"方法名" withErrorCode:errorCode];
}];
}
-(void)networkError:(NSString *)methodName withErrorCode:(NetErrorCode)errorCode{
if(self.delegate){
[self.delegate networkModelWithNetError:errorCode withMethodName:methodName];
}
}
@end
上面的.m文件已经很明白了,通过delegate将网络请求的数据传递出去,而不需要关心delegate的实现者是谁。同时,此类中,对于网络数据的处理和解析,可以独自完成,以实现最大程度的独立封装。
接下来就是如何在ViewController中使用了。
//ViewController.h
@interface ViewController : UIViewController{
TestNetworkModel *_networkModel;
}
@end
//ViewController.m
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
_networkModel = [[TestNetworkModel alloc]init];
_networkModel.delegate = self;
[_networkModel queryDataWithXML:@"方法名1"];
[_networkModel queryDataWithProtocolBuffer:@"方法名2"];
}
#pragma mark - 网络请求的返回s
- (void)networkModelWithNetDataResponse:(NSData *)response withMethodName:(NSString *)methodName{
}
- (void)networkModelWithNetResponse:(NSDictionary *)response withMethodName:(NSString *)methodName{
}
- (void)networkModelWithNetError:(NetErrorCode)errorCode withMethodName:(NSString *)methodName{
}
@end
上面已经很清楚了,ViewController通过对TestNetworkModel的实例来发起网络请求,网络请求的数据通过TestNetworkModel中的delegate,也就是ViewController来处理网路请求解析后的数据,以供View的处理和显示。
至此,整个流程已经完整了,写的比较简单,只为了告诉大家这个实现的过程而已。其实,如果不想数据这么直接暴露给ViewController,实际可以在ViewController和NetworkModel直接再创建一层对数据的加工和逻辑处理层,而只暴露给ViewController需要显示的东西。
其实用NetworkModel的思路,也是为了方便网络请求的重用,同一个请求可能再多个地方调用,这样只需要在不同的地方创建不同的NetworkModel实例就可以了,最大程度的减少耦合。