代碼地址: https://github.com/huangxiongbiao12/BGNetworking
1、封裝AFNetworking基本請求方法
封裝原因:當AFNetworking的api改變的時候只需要更改封裝的方法,無需大面積改動代碼(不封裝直接用AFNetworking請求如果AFNetworking的api變化所有用的地方都需要變化)
代碼依賴了MBProgressHUD,顯示加載圖
封裝思路接口:
1、主要封裝了Get Post請求帶HUD和不帶HUD的另外一個可以選擇是否帶HUD的接口
2、將上傳圖片的單獨封裝
3、封裝了https加載證書驗證的方法,只需要設置cer文件的路徑名稱,以及是否需要https驗證
代碼如下:
api
#import <Foundation/Foundation.h>
#import "NetStatusData.h"
@interface HttpRequstData : NSObject
+ (void)getUrl:(NSString*)url parameters:(NSDictionary*)parameters success:(void(^)(id responseObject)) success failure:(void(^)(NSError *error)) failure;
+(void)getNoHUDUrl:(NSString*)url parameters:(NSDictionary*)parameters success:(void(^)(id responseObject)) success failure:(void(^)(NSError *error)) failure;
+(void)postUrl:(NSString*)url parameters:(NSDictionary*)parameters showHUD:(BOOL)show success:(void(^)(id responseObject)) success failure:(void(^)(NSError *error)) failure;
+(void)postUrl:(NSString*)url parameters:(NSDictionary*)parameters success:(void(^)(id responseObject)) success failure:(void(^)(NSError *error)) failure;
+(void)postNoHUDUrl:(NSString*)url parameters:(NSDictionary*)parameters success:(void(^)(id responseObject)) success failure:(void(^)(NSError *error)) failure;
+ (nullable NSURLSessionDataTask *)POST:(NSString *)URLString
parameters:(nullable id)parameters
constructingBodyWithBlock:(nullable void (^)(id <AFMultipartFormData> formData))block
success:(nullable void (^)(NSURLSessionDataTask *task, id _Nullable responseObject))success
failure:(nullable void (^)(NSURLSessionDataTask * _Nullable task, NSError *error))failure;
+ (nullable NSURLSessionDataTask *)POST:(NSString *)URLString
parameters:(nullable id)parameters
constructingBodyWithBlock:(nullable void (^)(id <AFMultipartFormData> formData))block
progress:(nullable void (^)(NSProgress *uploadProgress))uploadProgress
success:(nullable void (^)(NSURLSessionDataTask *task, id _Nullable responseObject))success
failure:(nullable void (^)(NSURLSessionDataTask * _Nullable task, NSError *error))failure;
@end
實現
#import "HttpRequstData.h"
/**
* 是否開啟https SSL 驗證
*
* @return YES為開啟,NO為關閉
*/
#define openHttpsSSL YES
/**
* SSL 證書名稱,僅支持cer格式。“app.bishe.com.cer”,則填“app.bishe.com”
*/
#define certificate @"public"
typedef NS_ENUM(NSInteger,HttpType) {
GET,
POST
};
static int timeOut = 30;
@interface HttpRequstData ()
{
NSString *_url;
NSDictionary *_parameters;
}
@property(nonatomic,strong)UILabel *tipsLable;
@end
@implementation HttpRequstData
+(void)getUrl:(NSString*)url parameters:(NSDictionary*)parameters success:(void(^)(id responseObject)) success failure:(void(^)(NSError *error)) failure
{
[[NetStatusData shareNetStatus] checkNetStatus];
AFHTTPSessionManager *manager = [AFHTTPSessionManager manager];
manager.requestSerializer.timeoutInterval = timeOut;
// 加上這行代碼,https ssl 驗證。
if(openHttpsSSL)
{
[manager setSecurityPolicy:[self customSecurityPolicy]];
}
[MBProgressHUD showMessage:@"加載中..."];
DDLog(@"parameters:%@",parameters);
[manager GET:url parameters:parameters progress:^(NSProgress * _Nonnull downloadProgress) {
} success:^(NSURLSessionDataTask * _Nonnull task, id _Nullable responseObject) {
DDLog(@"====%@,%@==url:%@",responseObject,parameters,url);
[MBProgressHUD hideHUD];
//登陸成功===================================
if ([responseObject[@"status"] isEqualToNumber:@500]) {
[MBProgressHUD showSuccess:@"加載成功"];
if (success) {
success(responseObject);
}
}else{//登陸失敗=============================
if ([responseObject[@"data"] isKindOfClass:[NSString class]]) {
[MBProgressHUD showError:responseObject[@"data"]];
}else{
[MBProgressHUD showError:@"信息錯誤"];
}
NSError *error1;
failure(error1);
}
} failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {
// [[NetWorkingStausTipLabel shareNetWorkingStausTipLabel] showDuration:2.0];
[MBProgressHUD hideHUD];
[MBProgressHUD showError:@"網絡異常"];
DDLog(@"%s==error--%@",__func__,error);
if (error) {
failure(error);
}
}];
}
+(void)getNoHUDUrl:(NSString*)url parameters:(NSDictionary*)parameters success:(void(^)(id responseObject)) success failure:(void(^)(NSError *error)) failure {
[[NetStatusData shareNetStatus] checkNetStatus];
AFHTTPSessionManager *manager = [AFHTTPSessionManager manager];
manager.requestSerializer.timeoutInterval = timeOut;
// 加上這行代碼,https ssl 驗證。
if(openHttpsSSL)
{
[manager setSecurityPolicy:[self customSecurityPolicy]];
}
DDLog(@"parameters:%@",parameters);
[manager GET:url parameters:parameters progress:^(NSProgress * _Nonnull downloadProgress) {
} success:^(NSURLSessionDataTask * _Nonnull task, id _Nullable responseObject) {
DDLog(@"====%@,%@==url:%@",responseObject,parameters,url);
[MBProgressHUD hideHUD];
//登陸成功===================================
if ([responseObject[@"status"] isEqualToNumber:@500]) {
if (success) {
success(responseObject);
}
}else{//登陸失敗=============================
if ([responseObject[@"data"] isKindOfClass:[NSString class]]) {
[MBProgressHUD showError:responseObject[@"data"]];
}else{
[MBProgressHUD showError:@"信息錯誤"];
}
NSError *error1;
failure(error1);
}
} failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {
// [[NetWorkingStausTipLabel shareNetWorkingStausTipLabel] showDuration:2.0];
DDLog(@"%s==error--%@",__func__,error);
[MBProgressHUD hideHUD];
[MBProgressHUD showError:@"網絡異常"];
if (error) {
failure(error);
}
}];
}
#pragma mark-------封裝請求
+(void)postUrl:(NSString*)url parameters:(NSDictionary*)parameters showHUD:(BOOL)show success:(void(^)(id responseObject)) success failure:(void(^)(NSError *error)) failure {
[[NetStatusData shareNetStatus] checkNetStatus];
AFHTTPSessionManager *manager = [AFHTTPSessionManager manager];
manager.requestSerializer.timeoutInterval = timeOut;
// 加上這行代碼,https ssl 驗證。
if(openHttpsSSL)
{
[manager setSecurityPolicy:[self customSecurityPolicy]];
}
if (show) {
[MBProgressHUD showMessage:@"加載中..."];
}
DDLog(@"parameters:%@==url:%@==",parameters,url);
[manager POST:url parameters:parameters progress:^(NSProgress * _Nonnull uploadProgress) {
} success:^(NSURLSessionDataTask * _Nonnull task, id _Nullable responseObject) {
DDLog(@"====%@,%@==url:%@===parameters:%@",responseObject,responseObject[@"data"],url,parameters);
if (show) {
[MBProgressHUD hideHUD];
}
//登陸成功===================================
if ([responseObject[@"status"] isEqualToNumber:@500]) {
if ([responseObject[@"data"] isKindOfClass:[NSArray class]]) {
if ([responseObject[@"data"] count] < 1) {
// [MBProgressHUD showSuccess:@"沒有更多數據"];
}else{
if (show) {
// [MBProgressHUD showSuccess:@"加載成功"];
}
}
}else{
if (show) {
// [MBProgressHUD showSuccess:@"加載成功"];
}
}
if (success) {
success(responseObject);
}
}else{//登陸失敗=============================
if ([responseObject[@"data"] isKindOfClass:[NSString class]]) {
// if (show) {
[MBProgressHUD showError:responseObject[@"data"]];
// }
}else{
// if (show) {
[MBProgressHUD showError:@"信息錯誤"];
// }
}
NSError *error1;
failure(error1);
}
} failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {
// [[NetWorkingStausTipLabel shareNetWorkingStausTipLabel] showDuration:2.0];
if (show) {
[MBProgressHUD hideHUD];
}
// [MBProgressHUD showError:@"請求失敗"];
DDLog(@"%s==error--%@",__func__,error);
if (error) {
failure(error);
}
}];
}
+(void)postUrl:(NSString*)url parameters:(NSDictionary*)parameters success:(void(^)(id responseObject)) success failure:(void(^)(NSError *error)) failure
{
[self postUrl:url parameters:parameters showHUD:YES success:success failure:failure];
}
+(void)postNoHUDUrl:(NSString*)url parameters:(NSDictionary*)parameters success:(void(^)(id responseObject)) success failure:(void(^)(NSError *error)) failure
{
[self postUrl:url parameters:parameters showHUD:NO success:success failure:failure];
}
#pragma mark-------上傳圖片
+(NSURLSessionDataTask *)POST:(NSString *)URLString parameters:(id)parameters constructingBodyWithBlock:(void (^)(id<AFMultipartFormData>))block progress:(void (^)(NSProgress *))uploadProgress success:(void (^)(NSURLSessionDataTask *, id _Nullable))success failure:(void (^)(NSURLSessionDataTask * _Nullable, NSError *))failure {
AFHTTPSessionManager *manager = [AFHTTPSessionManager manager];
// 加上這行代碼,https ssl 驗證。
if(openHttpsSSL)
{
[manager setSecurityPolicy:[self customSecurityPolicy]];
}
return [manager POST:URLString parameters:parameters constructingBodyWithBlock:block progress:uploadProgress success:success failure:failure];
}
+(NSURLSessionDataTask *)POST:(NSString *)URLString parameters:(id)parameters constructingBodyWithBlock:(void (^)(id<AFMultipartFormData>))block success:(void (^)(NSURLSessionDataTask *, id _Nullable))success failure:(void (^)(NSURLSessionDataTask * _Nullable, NSError *))failure {
return [self POST:URLString parameters:parameters constructingBodyWithBlock:block progress:nil success:success failure:failure];
}
#pragma mark========https
+ (AFSecurityPolicy*)customSecurityPolicy
{
// /先導入證書
NSString *cerPath = [[NSBundle mainBundle] pathForResource:certificate ofType:@"cer"];//證書的路徑
NSData *certData = [NSData dataWithContentsOfFile:cerPath];
// AFSSLPinningModeCertificate 使用證書驗證模式
AFSecurityPolicy *securityPolicy = [AFSecurityPolicy policyWithPinningMode:AFSSLPinningModeCertificate];
// AFSSLPinningModeNone、AFSSLPinningModeCertificate
// allowInvalidCertificates 是否允許無效證書(也就是自建的證書),默認為NO
// 如果是需要驗證自建證書,需要設置為YES
securityPolicy.allowInvalidCertificates = YES;
//validatesDomainName 是否需要驗證域名,默認為YES;
//假如證書的域名與你請求的域名不一致,需把該項設置為NO;如設成NO的話,即服務器使用其他可信任機構頒發的證書,也可以建立連接,這個非常危險,建議打開。
//置為NO,主要用于這種情況:客戶端請求的是子域名,而證書上的是另外一個域名。因為SSL證書上的域名是獨立的,假如證書上注冊的域名是www.google.com,那么mail.google.com是無法驗證通過的;當然,有錢可以注冊通配符的域名*.google.com,但這個還是比較貴的。
//如置為NO,建議自己添加對應域名的校驗邏輯。
securityPolicy.validatesDomainName = NO;
securityPolicy.pinnedCertificates = [[NSSet alloc] initWithObjects:certData,nil];
return securityPolicy;
}
2、封裝網絡狀態監測
思路:用了一個單例,每次請求接口的時候調用小網絡狀態監測api
根據不同的網絡情況顯示提示
#import <Foundation/Foundation.h>
@interface NetStatusData : NSObject
@property(nonatomic,assign)AFNetworkReachabilityStatus status;//網絡狀態
+(NetStatusData*)shareNetStatus;
-(void)checkNetStatus;//檢查網絡狀態
@end
static NetStatusData *_netStatus = nil;
@implementation NetStatusData
+(NetStatusData *)shareNetStatus {
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
if (!_netStatus) {
_netStatus = [NetStatusData new];
}
});
return _netStatus;
}
-(void)checkNetStatus {
AFNetworkReachabilityManager *manager=[AFNetworkReachabilityManager sharedManager];
__weak AFNetworkReachabilityManager *weakManager = manager;
[manager startMonitoring];
//檢測網絡狀態有網絡,網絡請求數據,沒網絡提示
[manager setReachabilityStatusChangeBlock:^(AFNetworkReachabilityStatus status) {
self.status = status;
if (!status) {//未連接
UIWindow *window = [[UIApplication sharedApplication].windows lastObject];
UILabel *tipsLable;
if (!tipsLable) {
tipsLable = [[UILabel alloc] initWithFrame:(CGRect){kScreenWidth/2,kScreenHeight/2+80,110,33}];
tipsLable.textColor = [UIColor whiteColor];
tipsLable.textAlignment = NSTextAlignmentCenter;
tipsLable.centerX = window.centerX;
tipsLable.backgroundColor = [UIColor blackColor];
tipsLable.text = @"網絡未連接";
}
[window addSubview:tipsLable];
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(3 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
[tipsLable removeFromSuperview];
});
return;
}else{//連接請求數據
// [self requstData:type];
}
[weakManager stopMonitoring];
}];
}
@end
3、數據解析層(核心思想)
注:利用基類做類別封裝請求,可以直接用模型請求接口,并且封裝解析過程。根據不同的模型及json類型動態解析數據。后期代碼免去解析代碼
代碼分為兩部分,一部分代碼解析,一部分數據請求
1、數據請求
#import <Foundation/Foundation.h>
@interface NSObject (Request)
//請求數據
+(void)requestUrl:(NSString*)url parameters:(NSDictionary*)parameters showHUD:(BOOL)show success:(void(^)(id responseObject)) success failure:(void(^)(NSError *error)) failure;
+(void)requestUrl:(NSString*)url parameters:(NSDictionary*)parameters success:(void(^)(id responseObject)) success failure:(void(^)(NSError *error)) failure;
+(void)requestNoHUDUrl:(NSString*)url parameters:(NSDictionary*)parameters success:(void(^)(id responseObject)) success failure:(void(^)(NSError *error)) failure;
//解析數據
+(id)parseData:(id)responseObject;
//對象轉json
-(NSDictionary*)toJson;
//獲取屬性的類型
+(NSString*)getPropertyType:(NSString*)property;
-(NSString*)getPropertyType:(NSString*)property;
@end
#import "NSObject+Request.h"
#import <objc/runtime.h>
@implementation NSObject (Request)
+(void)requestUrl:(NSString*)url parameters:(NSDictionary*)parameters showHUD:(BOOL)show success:(void(^)(id responseObject)) success failure:(void(^)(NSError *error)) failure {
[HttpRequstData postUrl:url parameters:parameters showHUD:show success:^(id responseObject) {
id obj = [self parseData:responseObject[@"data"]];
success(obj);
} failure:^(NSError *error) {
failure(error);
}];
}
+(void)requestUrl:(NSString*)url parameters:(NSDictionary*)parameters success:(void(^)(id responseObject)) success failure:(void(^)(NSError *error)) failure {
[self requestUrl:url parameters:parameters showHUD:YES success:success failure:failure];
}
+(void)requestNoHUDUrl:(NSString *)url parameters:(NSDictionary *)parameters success:(void (^)(id))success failure:(void (^)(NSError *))failure {
[self requestUrl:url parameters:parameters showHUD:NO success:success failure:failure];
}
+(id)parseData:(id)responseObject {
id obj;
//單個對象
if ([responseObject isKindOfClass:[NSDictionary class]]) {
obj = [[self alloc]init];
[obj setValuesForKeysWithDictionary:responseObject];
}
// 多個對象
else if ([responseObject isKindOfClass:[NSArray class]]) {
obj = [NSMutableArray new];
for (NSDictionary *dic in responseObject) {
id o = [[self alloc]init];
[o setValuesForKeysWithDictionary:dic];
[obj addObject:o];
}
}
return obj;
}
-(NSDictionary *)toJson
{
NSMutableDictionary *params = [NSMutableDictionary dictionary];
unsigned int count = 0;
Ivar *ivars = class_copyIvarList(self.class, &count);
for (int i = 0; i < count; i++) {
const char *cname = ivar_getName(ivars[i]);
NSString *name = [NSString stringWithUTF8String:cname];
NSString *key = [name substringFromIndex:1];
id value = [self valueForKey:key];
if ([value isKindOfClass:[NSString class]]&&[(NSString*)value length]) {
[params setValue:value forKey:key];
}
}
return params;
}
#pragma mark-------//獲取屬性的類型
+(NSString *)getPropertyType:(NSString *)property {
//獲取對象的類型objc_getClass("UserModel")
objc_property_t p = class_getProperty(self, property.UTF8String);
if (!p) {
return nil;
}
const char *cname = property_getAttributes(p);
DDLog(@"%s==",cname);
// 2.成員類型
NSString *attrs = @(property_getAttributes(p));
NSUInteger dotLoc = [attrs rangeOfString:@","].location;
NSString *code = nil;
NSUInteger loc = 3;
if (dotLoc == NSNotFound) { // 沒有,
code = [attrs substringFromIndex:loc];
} else {
code = [attrs substringWithRange:NSMakeRange(loc, dotLoc - loc-1)];
}
DDLog(@"%@===%@====",code,attrs);
return code;
}
-(NSString *)getPropertyType:(NSString *)property {
//獲取對象的類型objc_getClass("UserModel")
objc_property_t p = class_getProperty(self.class, property.UTF8String);
if (!p) {
return nil;
}
const char *cname = property_getAttributes(p);
DDLog(@"%s==",cname);
// 2.成員類型
NSString *attrs = @(property_getAttributes(p));
NSUInteger dotLoc = [attrs rangeOfString:@","].location;
NSString *code = nil;
NSUInteger loc = 3;
if (dotLoc == NSNotFound) { // 沒有,
code = [attrs substringFromIndex:loc];
} else {
code = [attrs substringWithRange:NSMakeRange(loc, dotLoc - loc-1)];
}
DDLog(@"%@===%@====",code,attrs);
return code;
}
@end
2、數據解析
思路:通過kvc解析數據,判斷json的key值是否是json和數組是的話進一步解析。不是的話直接賦值,解決復雜類型的json動態解析。
注意:數據字段的時候需要指明數據放的對象類型
使用時需要解析的模型類需要繼承Model類
/*
*對象中含有的模型數據對應的對象名稱例如
*@{
@"statuses" : @"Status",===字段有statuses的數組含有的對象為Status類型
*};
*/
@property(nonatomic,strong)NSDictionary *classInArrayDic;
代碼如下
#import <Foundation/Foundation.h>
#import "NSObject+Request.h"
@interface Model : NSObject<NSCoding,NSCopying>
//對象轉json
-(NSDictionary*)toJson;
/*
*對象中含有的模型數據對應的對象名稱例如
*@{
@"statuses" : @"Status",===字段有statuses的數組含有的對象為Status類型
*};
*/
@property(nonatomic,strong)NSDictionary *classInArrayDic;
@end
#import "Model.h"
@implementation Model
-(void)setValue:(id)value forKey:(NSString *)key{
if ([value isKindOfClass:[NSNumber class]]) {
[self setValue:[NSString stringWithFormat:@"%@",value] forKey:key];
}else if ([value isKindOfClass:[NSArray class]]) {
}else{
if (!value||[value isKindOfClass:[NSNull class]]) {
[super setValue:@"" forKey:key];
}else{
[super setValue:value forKey:key];
}
}
}
-(void)setValue:(id)value forUndefinedKey:(NSString *)key{
if ([key isEqualToString:@"description"]) {
[self setValue:value forKey:@"descriptionStr"];
}else if ([key isEqualToString:@"id"]){
[self setValue:value forKey:@"Id"];
}else if([key isEqualToString:@"newPrice"]){
[self setValue:value forKey:@"price"];
}else if([key isEqualToString:@"code"]){
[self setValue:value forKey:@"codePara"];
}else{
DDLog(@"沒有key==%@",key);
}
}
-(void)setValuesForKeysWithDictionary:(NSDictionary<NSString *,id> *)keyedValues {
for (NSString *key in keyedValues) {
id value = keyedValues[key];
//對象中含有對象數組
if ([value isKindOfClass:[NSArray class]]) {
if (![self.classInArrayDic objectForKey:key]) {
DDLog(@"請給數組對象對應類型");
continue;
}
//設置數組的值
NSMutableArray *values = [NSMutableArray new];
//取出數組含有對象類型
Class ObjectType = NSClassFromString(self.classInArrayDic[key]);
DDLog(@"%@",ObjectType);
for (NSDictionary *dic in value) {
//獲取對象類型生成實例
id obj = [[ObjectType alloc]init];
[obj setValuesForKeysWithDictionary:dic];
[values addObject:obj];
}
[self setValue:values forKey:key];
}else if ([value isKindOfClass:[NSDictionary class]]) {//對象中含有對象
//獲取對象的類型
NSString *type = [self getPropertyType:key];
if (type.length) {
DDLog(@"請給對象對應類型");
continue;
}
//獲取對象類型生成實例
Class ObjectType = NSClassFromString(type);
id obj = [[ObjectType alloc]init];
[obj setValuesForKeysWithDictionary:value];
[self setValue:obj forKey:key];
}else {
[self setValue:value forKey:key];
}
}
}
-(NSDictionary *)toJson
{
NSMutableDictionary *params = [NSMutableDictionary dictionary];
unsigned int count = 0;
Ivar *ivars = class_copyIvarList(self.class, &count);
for (int i = 0; i < count; i++) {
const char *cname = ivar_getName(ivars[i]);
NSString *name = [NSString stringWithUTF8String:cname];
NSString *key = [name substringFromIndex:1];
id value = [self valueForKey:key];
if ([value isKindOfClass:[NSString class]]&&[(NSString*)value length]) {
[params setValue:value forKey:key];
}
}
return params;
}
#pragma mark-------NSCoding,歸接檔協議,運行時
- (void)encodeWithCoder:(NSCoder *)aCoder
{
unsigned int count = 0;
Ivar *ivars = class_copyIvarList(self.class, &count);
for (int i = 0; i < count; i++) {
const char *cname = ivar_getName(ivars[i]);
NSString *name = [NSString stringWithUTF8String:cname];
NSString *key = [name substringFromIndex:1];
id value = [self valueForKey:key];
[aCoder encodeObject:value forKey:key];
}
}
- (nullable instancetype)initWithCoder:(NSCoder *)aDecoder
{
if (self = [super init]) {
unsigned int count = 0;
Ivar *ivars = class_copyIvarList(self.class, &count);
for (int i = 0; i < count; i++) {
const char *cname = ivar_getName(ivars[i]);
NSString *name = [NSString stringWithUTF8String:cname];
NSString *key = [name substringFromIndex:1];
id value = [aDecoder decodeObjectForKey:key];
[self setValue:value forKey:key];
}
}
return self;
}
-(id)copyWithZone:(NSZone *)zone {
id copy = [[[self class] allocWithZone:zone] init];
unsigned int count = 0;
Ivar *ivars = class_copyIvarList(self.class, &count);
for (int i = 0; i < count; i++) {
const char *cname = ivar_getName(ivars[i]);
NSString *name = [NSString stringWithUTF8String:cname];
NSString *key = [name substringFromIndex:1];
id value = [self valueForKey:key];
[copy setValue:value forKey:key];
}
return copy;
}
@end