iOS 网络请求封装 OC 与 Swift

最近公司项目闲下来,业余时间自己写了个请求封装类(OC / SWIFT),供大家参考,有不好的地方请大家见谅。

一、先给大家介绍一下OC VNHttpRequestManager 封装类

cocopods集成
https://github.com/guohongqi-china/VNHttpRequest

此类包含以下特点:

  • 丰富的请求类型。常用请求类型,除了get、post 之外还支持 put、delete、patch、head等请求方式。
  • 全面的参数提交类型。正常情况下,form 表单提交参数方式,我们大多数使用在文件交互方面;但是极少数情况下我们可能会使用到 form 表单 进行数据交互;此时如果提交方式不匹配,可能会存在接口不通或者接口通了,但是返回的数据状态不正确。
  • 上传文件优化。批次上传图片,并对较大的图片做压缩优化;视频、音频也支持多批次。
  • 支持断点下载文件。
  • 丰富的错误信息采集。ServerResponseInfo 类采集了 http 响应码(响应码对应状态提示)、AFNet 响应码(响应码对应状态提示)、响应头信息。有些接口请求ResponseBody是不返回信息的,而是通过ResponseHeaders返回。
  • 日志中的 Unicode 编码优化。某些情况,我们从后台获取的数据打印日志为Unicode编码,此类做了日志转码优化操作,使我们更能清晰的看到响应内容。
    对于某些特殊的请求需要设置 请求头,或者请求超时时间(默认超时时间 10 s) 可在 + (void)requestSerializerSetting:(AFHTTPRequestSerializer *)requestSerializer;.m 文件方法中设置
.h 文件
//
//  Created by guohq on 2018/6/27.
//  Copyright © 2018年 guohq. All rights reserved.
//

#import <Foundation/Foundation.h>
@class ServerResponseInfo;

// 请求方式类型枚举
typedef NS_ENUM(NSInteger,RequestMethod){
    RequestMethod_Get = 100,
    RequestMethod_Post,
    RequestMethod_Put,
    RequestMethod_Delete,
    RequestMethod_Patch,
    RequestMethod_HEAD,
    
};

// 请求方式类型枚举
typedef NS_ENUM(NSInteger,BodyType){
    BodyType_JSON = 1000,
    BodyType_FORM,
};

// 请求类型枚举
typedef NS_ENUM(NSInteger,RequestType){
    RequestType_UPLOAD = 10000, //上传文件
    RequestType_REQUEST    //交互
};

// 文件类型
typedef NS_ENUM(NSInteger,FileType){
    FileType_Video = 100000, //上传视频、音频
    FileType_Image    //上传图片
};

//  请求成功回调
typedef void(^resultBlock)(ServerResponseInfo * _Nullable serverInfo);


/**
 * 注意:如有特殊请求头要求请在 .m文件 requestSerializerSetting 方法里面设置。
 */

@interface VNHttpRequestManager : NSObject


/**
 JSON 参数
 
 @param requestMethod 请求类型(枚举值)
 @param params 请求参数
 @param pathUrl 请求链接
 @param result 数据回调
 */
+(void)sendJSONRequestWithMethod:(RequestMethod )requestMethod
                         pathUrl:(NSString *__nonnull)pathUrl
                          params:(NSDictionary *_Nullable)params
                      complement:(resultBlock __nonnull)result;

/**
 JSON 加密 参数
 
 @param requestMethod 请求类型(枚举值)
 @param requestData 加密之后的参数数据
 @param pathUrl 请求链接
 @param result 数据回调
 */
+(void)sendJSONRequestWithMethod:(RequestMethod )requestMethod
                         pathUrl:(NSString *__nonnull)pathUrl
                     requestData:(NSData *_Nullable)requestData
                      complement:(resultBlock __nonnull)result;

/**
 FormData 参数类型
 
 @param requestMethod 请求类型(枚举值)
 @param params 请求参数
 @param pathUrl 请求链接
 @param result 数据回调
 */
+(void)sendFORMRequestWithMethod:(RequestMethod )requestMethod
                         pathUrl:(NSString *__nonnull)pathUrl
                          params:(NSDictionary *_Nullable)params
                      complement:(resultBlock __nonnull)result;

/**
 批次上传文件
 
 @param fileArray 文件路径数组 注意:fileArray 里面的文件类型要保持一致
 @param bodDic 请求参数
 @param path 请求链接
 @param result 数据回调
 @param fileType 上传文件的类型   图片 or  视/音频
 */
+ (void)uploadFileWithPath:(NSString *__nonnull)path
                  filePath:(NSArray *__nonnull)fileArray
                     parms:(NSDictionary *_Nullable)bodDic
                  fileType:(FileType)fileType
                    result:(resultBlock __nonnull)result;

/**
 文件下载
 
 @param pathUrl 下载路径
 @param filePath 下载路径 可传 null
 @param complement 数据回调
 */
+ (NSURLSessionDownloadTask *_Nullable)downLoadRequest:(NSString *__nonnull)pathUrl
                                              filePath:(NSString *_Nullable)filePath
                                          downProgress:(void (^_Nullable)(double progress))downProgress
                                            complement:(void (^_Nullable)(NSURLResponse * _Nonnull response, NSURL * _Nullable filePath, NSError * _Nullable error))complement;

/**
 请求管理者特殊要求头部设置(未作特殊要求,不用调用次函数,一般全局只需调用一次)
 
 @param headerFields 下载路径
 
 */
+ (void)requestHeaderWidth:(NSDictionary *_Nullable)headerFields;

/**
 一般请求头全局只需设置一次,不要多次设置,如果需要多次设置请求头请设置为NO;
 
 @param result 是否需要删除请求头,不删除就会多次设置,浪费时间
 
 */
//+ (void)deleteHeaderSeting:(BOOL)result;


/**
 开始下载
 @param downloadTask 下载管理者
 */
+ (void)startResume:(NSURLSessionDownloadTask *_Nullable)downloadTask;

/**
 下载暂停
 @param downloadTask 下载管理者
 */
+ (void)suspend:(NSURLSessionDownloadTask *_Nullable)downloadTask;

/**
 取消请求
 */
+ (void)cancleRequestWork;

/**
 网络状态监听
 */
+ (void)netWorkReachability:(void(^_Nullable)(NSString * _Nullable))currentStatus;






@end



// 错误信息 详情
@interface ServerResponseInfo : NSObject

@property (nonatomic,getter=isSuccess) BOOL responeStatus;           //响应是否成功

@property (nonatomic, strong) id _Nullable response;                           //成功响应数据
@property (nonatomic, strong) id _Nullable errorData;
@property (nonatomic, assign) NSInteger httpCode;                    // http 响应码
@property (nonatomic, copy) NSString * _Nullable errorMessage;                  // 响应提示
@property (nonatomic, copy) NSString * _Nullable httpMessage;                   // http 响应提示

@property (nonatomic, strong) NSDictionary * _Nullable responseHeader;          // 响应header


@end

.m
//
//  VNHttpRequestManager.m
//  声未识别
//
//  Created by guohq on 2018/6/27.
//  Copyright © 2018年 guohq. All rights reserved.
//



#import "VNHttpRequestManager.h"
#import "AFNetworking.h"
#import "VNRequestOperation.h"

static NSMutableDictionary *headerDic;
//setting request header then delete headerDic , default YES,
static BOOL         setedDelete = NO;

@interface AFHttpClientManager : AFHTTPSessionManager

@property (nonatomic, strong) NSURLSessionDataTask *task;
@property (strong, nonatomic, nonnull) NSOperationQueue *requestQueue;


+(AFHttpClientManager *)sharedClient;

@end

@implementation AFHttpClientManager

static AFHttpClientManager *client = nil;
/**
 * 单例
 */
+(AFHttpClientManager *)sharedClient{
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        client = [AFHttpClientManager manager];
        
        //Https setting
        client.securityPolicy.allowInvalidCertificates = YES;
        client.securityPolicy     = [AFSecurityPolicy defaultPolicy];
        
        client.responseSerializer = [AFHTTPResponseSerializer serializer];
        
        client.requestQueue       = [[NSOperationQueue alloc]init];
        client.requestQueue.name  = @"com.vn.requetQueue";
        // 默认最大并发数为6
        client.requestQueue.maxConcurrentOperationCount    =  6;
        
        //response data type
        client.responseSerializer.acceptableContentTypes = [NSSet setWithObjects:@"application/json", @"text/json", @"text/html", @"text/javascript",@"text/plain",@"image/gif", nil];
        
    });
    
    return client;
}

// 不同请求头部设置
+ (void)requestSerializerSetting:(AFHTTPRequestSerializer *)requestSerializer{
    
    //time out 超时时间
    [requestSerializer willChangeValueForKey:@"timeoutInterval"];
    requestSerializer.timeoutInterval = 20.f;
    [requestSerializer didChangeValueForKey:@"timeoutInterval"];
    @synchronized (client.requestSerializer) {
        if (headerDic.allKeys.count) {
            for (NSString *key in headerDic) {
                [client.requestSerializer setValue:headerDic[key] forHTTPHeaderField:key];
            }
        }
    }
    
}

+ (NSMutableURLRequest *)requestData:(NSData *)data with:(NSString *)url paramType:(BodyType)type{
    NSMutableURLRequest *request ;
    
    if (type == BodyType_JSON) {
        request = [[AFJSONRequestSerializer serializer] requestWithMethod:@"POST" URLString:url parameters:nil error:nil];
    }else{
        request = [[AFHTTPRequestSerializer serializer] requestWithMethod:@"POST" URLString:url parameters:nil error:nil];
    }
    
    // 超时设置
    [request willChangeValueForKey:@"timeoutInterval"];
    request.timeoutInterval= 30.0f;
    [request didChangeValueForKey:@"timeoutInterval"];
    
    [request setValue:@"application/json" forHTTPHeaderField:@"Content-Type"];
    
    @synchronized (client.requestSerializer) {
        if (headerDic.allKeys.count) {
            for (NSString *key in headerDic) {
                [client.requestSerializer setValue:headerDic[key] forHTTPHeaderField:key];
            }
        }
    }
    
    // 设置body
    [request setHTTPBody:data];
    
    return request;
}


@end



@implementation VNHttpRequestManager

//JSON 参数类型
+(void)sendJSONRequestWithMethod:(RequestMethod )requestMethod
                         pathUrl:(NSString *__nonnull)pathUrl
                          params:(NSDictionary *_Nullable)params
                      complement:(resultBlock __nonnull)result{
    
    AFHttpClientManager *client = [AFHttpClientManager sharedClient];
    // json格式 请求参数
    client.requestSerializer = [AFJSONRequestSerializer serializer];
    [client.requestSerializer setValue:@"application/json;charset=utf-8" forHTTPHeaderField:@"Content-Type"];
    
    typeof(client) weakClient = client;
    VNRequestOperation<VNOperationAdapter> *requestOperation = [[VNRequestOperation alloc]initOperationWithTask:^{
        // 请求头设置
        [AFHttpClientManager  requestSerializerSetting:weakClient.requestSerializer];
        
        [VNHttpRequestManager requestWidth:requestMethod requestManager:weakClient pathUrl:pathUrl params:params requestCount:0 complement:result];
    }];
    
    [client.requestQueue addOperation:requestOperation];
}

//JSON 参数加密类型
+(void)sendJSONRequestWithMethod:(RequestMethod )requestMethod
                         pathUrl:(NSString *__nonnull)pathUrl
                     requestData:(NSData *_Nullable)requestData
                      complement:(resultBlock __nonnull)result{
    
    AFHttpClientManager *client = [AFHttpClientManager sharedClient];
    
    //对url 进行汉字转码 iOS 9.0
    NSString *serverUrl = [pathUrl stringByAddingPercentEncodingWithAllowedCharacters:[NSCharacterSet URLQueryAllowedCharacterSet]];
    NSMutableURLRequest *request = [AFHttpClientManager requestData:requestData with:serverUrl paramType:BodyType_JSON];
    
    // 请求头设置
    [AFHttpClientManager requestSerializerSetting:client.requestSerializer];
    
    __block NSURLSessionDataTask *dataTask = nil;
    
    dataTask =  [client dataTaskWithRequest:[request copy] uploadProgress:^(NSProgress * _Nonnull uploadProgress) {
        
    } downloadProgress:^(NSProgress * _Nonnull downloadProgress) {
        
    } completionHandler:^(NSURLResponse * _Nonnull response, id  _Nullable responseObject, NSError * _Nullable error) {
        if (error) {
#ifdef DEBUG
            NSLog(@"URL Value=%@",serverUrl);
#endif
            [VNHttpRequestManager handleFailTask:dataTask error:error complement:result];
        }else{
            // 服务器响应 log 数据
            
#ifdef DEBUG
            NSLog(@"URL Value=%@",serverUrl);
#endif
            [VNHttpRequestManager handleSuccessTask:dataTask responseObject:responseObject complement:result];
        }
    }];
    
}


//FORM 参数类型
+(void)sendFORMRequestWithMethod:(RequestMethod )requestMethod
                         pathUrl:(NSString *__nonnull)pathUrl
                          params:(NSDictionary *_Nullable)params
                      complement:(resultBlock __nonnull)result{
    
    AFHttpClientManager *client = [AFHttpClientManager sharedClient];
    // formData格式 请求参数
    client.requestSerializer = [AFHTTPRequestSerializer serializer];
    [client.requestSerializer setValue:@"application/x-www-form-urlencoded" forHTTPHeaderField:@"Content-Type"];
    // 请求头设置
    typeof(client) weakClient = client;
    VNRequestOperation<VNOperationAdapter> *requestOperation = [[VNRequestOperation alloc]initOperationWithTask:^{
        [AFHttpClientManager  requestSerializerSetting:weakClient.requestSerializer];
        [VNHttpRequestManager requestWidth:requestMethod requestManager:weakClient pathUrl:pathUrl params:params requestCount:0 complement:result];
    }];
    [client.requestQueue addOperation:requestOperation];
}

//上传文件
+ (void)uploadFileWithPath:(NSString *__nonnull)path
                  filePath:(NSArray *__nonnull)fileArray
                     parms:(NSDictionary *_Nullable)bodDic
                  fileType:(FileType)fileType
                    result:(resultBlock __nonnull)result{
    
    AFHttpClientManager *client = [AFHttpClientManager sharedClient];
    // json格式 请求参数
    client.requestSerializer = [AFJSONRequestSerializer serializer];
    [client.requestSerializer setValue:@"application/json;charset=utf-8;" forHTTPHeaderField:@"Content-Type"];
    // 请求头设置
    [AFHttpClientManager  requestSerializerSetting:client.requestSerializer];
    
    
    [VNHttpRequestManager startRequestByMethod:RequestMethod_Post
                                requestManager:client
                                   requestPath:path
                                    bodyParams:bodDic
                                      fileType:fileType
                                   requestType:RequestType_UPLOAD
                                     fileArray:fileArray
                                    complement:result];
    
}

+ (NSURLSessionDownloadTask *)downLoadRequest:(NSString *__nonnull)pathUrl
                                     filePath:(NSString *_Nullable)filePath
                                 downProgress:(void (^)(double progress))downProgress
                                   complement:(void (^_Nullable)(NSURLResponse * _Nonnull response, NSURL * _Nullable filePath, NSError * _Nullable error))complement{
    
    AFHttpClientManager *client = [AFHttpClientManager sharedClient];
    // json格式 请求参数
    client.requestSerializer = [AFJSONRequestSerializer serializer];
    [client.requestSerializer setValue:@"application/json;charset=utf-8" forHTTPHeaderField:@"Content-Type"];
    
    // 请求头设置
    [AFHttpClientManager  requestSerializerSetting:client.requestSerializer];
    
    //请求
    NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:pathUrl]];
    
    
    NSURLSessionDownloadTask *downTask = [client downloadTaskWithRequest:request progress:^(NSProgress * _Nonnull downloadProgress) {
        downProgress(downloadProgress.completedUnitCount * 0.1 / (downloadProgress.totalUnitCount * 0.1));
        
    } destination:^NSURL * _Nonnull(NSURL * _Nonnull targetPath, NSURLResponse * _Nonnull response) {
        if (filePath) {
            return [NSURL fileURLWithPath:filePath];;
        }
        NSString *cachesPath = [NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) lastObject];
        NSString *path = [cachesPath stringByAppendingPathComponent:response.suggestedFilename];
        return [NSURL fileURLWithPath:path];
    } completionHandler:complement];
    
    return downTask;
}

// 开始下载
+ (void)startResume:(NSURLSessionDownloadTask *_Nullable)downloadTask{
    [downloadTask resume];
}

// 下载暂停
+ (void)suspend:(NSURLSessionDownloadTask *_Nullable)downloadTask{
    [downloadTask suspend];
}


// 网络请求取消
+ (void)cancleRequestWork{
    AFHttpClientManager *client = [AFHttpClientManager sharedClient];
    [client.task cancel];
}

+ (void)requestHeaderWidth:(NSDictionary *_Nullable)headerFields{
    headerDic = [headerFields mutableCopy];
}

+ (void)deleteHeaderSeting:(BOOL)result{
//    setedDelete = result;
}

#pragma mark------------------------------------  私有方法   --------------------------------------------------

// 响应重定向再次发起请求
+ (void)requestWidth:(RequestMethod)method
      requestManager:(AFHttpClientManager *)manager
             pathUrl:(NSString *)pathUrl
              params:(NSDictionary *)params
        requestCount:(NSInteger)requestCount
          complement:(resultBlock)result{
    __block NSInteger count = requestCount;
    [VNHttpRequestManager startRequestByMethod:method
                                requestManager:manager
                                   requestPath:pathUrl
                                    bodyParams:params
                                      fileType:0
                                   requestType:RequestType_REQUEST
                                     fileArray:nil
                                    complement:^(ServerResponseInfo *serverInfo) {
                                        //网络请求重定向
                                        if (serverInfo.httpCode == 302 && count < 2) {
                                            count ++;
                                            [VNHttpRequestManager requestWidth:method requestManager:manager pathUrl:pathUrl params:params requestCount:count complement:result];
                                            
                                        }else{
                                            [[NSOperationQueue mainQueue] addOperationWithBlock:^{
                                                result(serverInfo);
                                            }];
                                        }
                                        
                                    }];
    
}

/**
 服务器响应数据解析
 
 @param requestMethod 请求类型(枚举值)
 @param params 请求参数
 @param pathUrl 请求链接
 @param result 数据回调
 @param requestType 数据交互/上传文件
 @param fileArray 文件路径数组
 @param fileType 上传文件的类型(视频、图片)
 */

+ (void)startRequestByMethod:(RequestMethod)requestMethod
              requestManager:(AFHttpClientManager *)manager
                 requestPath:(NSString *)pathUrl
                  bodyParams:(NSDictionary *)params
                    fileType:(FileType)fileType
                 requestType:(RequestType)requestType
                   fileArray:(NSArray *)fileArray
                  complement:(resultBlock)result{
    
    //对url 进行汉字转码 iOS 9.0
    NSString *serverUrl = [pathUrl stringByAddingPercentEncodingWithAllowedCharacters:[NSCharacterSet URLQueryAllowedCharacterSet]];
    
    void (^successBlock)(NSURLSessionDataTask *,id) = ^(NSURLSessionDataTask * _Nonnull task, id  _Nullable responseObject) {
        
        // 服务器响应 log 数据
#ifdef DEBUG
        NSLog(@"URL Value=%@",serverUrl);
        NSLog(@"Parms Value=%@",params);
#endif
        [VNHttpRequestManager handleSuccessTask:task responseObject:responseObject complement:result];
        
    };
    
    void (^failureBlock)(NSURLSessionDataTask *,NSError *) =^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {
        
#ifdef DEBUG
        NSLog(@"URL Value=%@",serverUrl);
        NSLog(@"Parms Value=%@",params);
#endif
        [VNHttpRequestManager handleFailTask:task error:error complement:result];
        
        
    };
    
    switch (requestType) {
        case RequestType_REQUEST:
        {
            [VNHttpRequestManager startRequestByMethod:requestMethod requestManager:client requestPath:serverUrl bodyParams:params successBlock:successBlock failureBlock:failureBlock];
        }
            break;
        case RequestType_UPLOAD:
        {
            [VNHttpRequestManager uploadFileWithManager:client requestPath:serverUrl bodyParams:params fileArray:fileArray fileType:fileType successBlock:successBlock failureBlock:failureBlock];
        }
            break;
            
        default:
            break;
    }
    
    
}

+ (void)handleSuccessTask:(NSURLSessionDataTask * _Nonnull )task responseObject:(id  _Nullable)responseObject complement:(resultBlock)result{
    NSHTTPURLResponse *response = (NSHTTPURLResponse *)task.response;
    // 获取相应code值
    ServerResponseInfo *infoData = [ServerResponseInfo new];
    infoData.httpCode = response.statusCode;
    infoData.responseHeader = [VNHttpRequestManager josnSerialization:response.allHeaderFields];
    infoData.responeStatus = YES;
    
#ifdef DEBUG
    NSLog(@"ResponseStateCode Value=%ld",(long)infoData.httpCode);
#endif
    
    
    // 服务器数据解析
    [self getObjectFromJSONData:responseObject complement:^(NSError *error, id jsonObject) {
        NSDictionary *allHeaders = response.allHeaderFields;
        NSData *jsonData = [NSJSONSerialization dataWithJSONObject:allHeaders options:NSJSONWritingPrettyPrinted
                                                             error:&error];
        if (jsonObject) {
            //如果接口相应数据放在响应体(body)里面,走这里。
            
            infoData.response = jsonObject;
            infoData.errorData = error;
            
            result(infoData);
        }else{
            //如果接口相应数据放在响应头里面,走这里。
            [self getObjectFromJSONData:jsonData complement:^(NSError *error, id jsonObject) {
                infoData.response = jsonObject;
                infoData.errorData = error;
                result(infoData);
            }];
        }
        
        
    }];
}

+ (void)handleFailTask:(NSURLSessionDataTask * _Nonnull )task error:(NSError * _Nonnull)error complement:(resultBlock)result{
    NSHTTPURLResponse *response = (NSHTTPURLResponse *)task.response;
    
    
    ServerResponseInfo *infoData = [ServerResponseInfo new];
    infoData.httpCode = response.statusCode;
    infoData.responseHeader = [VNHttpRequestManager josnSerialization:response.allHeaderFields];
    infoData.responeStatus = NO;
    
#ifdef DEBUG
    NSLog(@"ResponseStateCode Value=%ld",(long)infoData.httpCode);
    for (id errorInfo in error.userInfo.allKeys) {
        
        id obj = error.userInfo[errorInfo];
        if ([obj isKindOfClass:NSData.class]) {
            NSString *resul1t =[[ NSString alloc] initWithData:error.userInfo[errorInfo] encoding:NSUTF8StringEncoding];
            obj = resul1t;
        }
        NSLog(@"Error %@ : %@",errorInfo, obj);
    }
#endif
    
    [self getErrorInfo:error infoData:infoData];
    infoData.errorData = error;
    
    result(infoData);
}

//发起请求
+ (void)startRequestByMethod:(RequestMethod)requestMethod
              requestManager:(AFHttpClientManager *)manager
                 requestPath:(NSString *)pathUrl
                  bodyParams:(NSDictionary *)params
                successBlock:(void (^)(NSURLSessionDataTask * _Nonnull task, id  _Nullable responseObject))successBlock
                failureBlock:(void (^)(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error))failureBlock {
    
    // 发起请求
    if (requestMethod == RequestMethod_Get) {
        manager.task = [manager GET:pathUrl parameters:params progress:nil success:successBlock failure:failureBlock];
    }else if (requestMethod == RequestMethod_Post) {
        manager.task = [manager POST:pathUrl parameters:params progress:nil success:successBlock failure:failureBlock];
    }else if (requestMethod == RequestMethod_Put) {
        manager.task = [manager PUT:pathUrl parameters:params success:successBlock failure:failureBlock];
    }else if (requestMethod == RequestMethod_Delete) {
        manager.task = [manager DELETE:pathUrl parameters:params success:successBlock failure:failureBlock];
    }else if (requestMethod == RequestMethod_Patch) {
        manager.task = [manager PATCH:pathUrl parameters:params success:successBlock failure:failureBlock];
    }else if (requestMethod == RequestMethod_HEAD){
        manager.task = [manager HEAD:pathUrl parameters:params success:^(NSURLSessionDataTask * _Nonnull task) {
            successBlock(task,nil);
        } failure:failureBlock];
    }
}



// 文件上传
+ (void)uploadFileWithManager:(AFHttpClientManager *)manager
                  requestPath:(NSString *)pathUrl
                   bodyParams:(NSDictionary *)params
                    fileArray:(NSArray *)fileArray
                     fileType:(FileType)fileType
                 successBlock:(void (^)(NSURLSessionDataTask * _Nonnull task, id  _Nullable responseObject))successBlock
                 failureBlock:(void (^)(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error))failureBlock{
    
    [manager POST:pathUrl parameters:params constructingBodyWithBlock:^(id<AFMultipartFormData>  _Nonnull formData) {
        
        if (fileType == FileType_Image) {
            [VNHttpRequestManager imgFormdata:formData fileArray:fileArray];
        }else{
            [VNHttpRequestManager vedioFormdata:formData fileArray:fileArray];
        }
        
    } progress:^(NSProgress * _Nonnull uploadProgress) {
        NSLog(@"上传进度:%f",uploadProgress.fractionCompleted);
    } success:successBlock failure:failureBlock];
    
}

//音、视频上传
+ (void)vedioFormdata:(id<AFMultipartFormData>  _Nonnull)formData
            fileArray:(NSArray *)fileArray{
    NSString *type = [[fileArray.firstObject componentsSeparatedByString:@"."] lastObject];
    for (NSString *filePath in fileArray) {
        
        NSDate *date = [NSDate date];
        NSDateFormatter *formormat = [[NSDateFormatter alloc]init];
        [formormat setDateFormat:@"yyyyMMddHHmmss"];
        NSString *dateString = [formormat stringFromDate:date];
        
        NSString *fileName = [NSString  stringWithFormat:@"%@.%@",dateString,type];
        
        [formData appendPartWithFileURL:[NSURL fileURLWithPath:filePath] name:@"file" fileName:fileName mimeType:@"wav/mp4/amr/mp3" error:nil];
        
    }
}
//图片上传处理
+ (void)imgFormdata:(id<AFMultipartFormData>  _Nonnull)formData
          fileArray:(NSArray *)fileArray{
    
    NSString *type = [[fileArray.firstObject componentsSeparatedByString:@"."] lastObject];
    
    for (NSString *imgPath in fileArray) {
        UIImage * image =[UIImage  imageWithContentsOfFile:imgPath];
        NSDate *date = [NSDate date];
        NSDateFormatter *formormat = [[NSDateFormatter alloc]init];
        [formormat setDateFormat:@"yyyyMMddHHmmss"];
        NSString *dateString = [formormat stringFromDate:date];
        
        NSString *fileName = [NSString  stringWithFormat:@"%@.%@",dateString,type];
        NSData *imageData = UIImageJPEGRepresentation(image, 1);
        double scaleNum = (double)300*1024/imageData.length;
        NSLog(@"图片压缩率:%f",scaleNum);
        if(scaleNum <1){
            
            imageData = UIImageJPEGRepresentation(image, scaleNum);
        }else{
            
            imageData = UIImageJPEGRepresentation(image, 0.1);
            
        }
        
        [formData  appendPartWithFileData:imageData name:@"image" fileName:fileName mimeType:@"image/jpg/png/jpeg"];
        
    }
}


// unicode 编码汉化
+(NSString *)replaceUnicode:(NSString*)unicodeStr{
    
    NSString *tempStr1=[unicodeStr stringByReplacingOccurrencesOfString:@"\\u"withString:@"\\U"];
    NSString *tempStr2=[tempStr1 stringByReplacingOccurrencesOfString:@"\""withString:@"\\\""];
    NSString *tempStr3=[[@"\""stringByAppendingString:tempStr2]stringByAppendingString:@"\""];
    NSData *tempData=[tempStr3 dataUsingEncoding:NSUTF8StringEncoding];
    NSString* returnStr = [NSPropertyListSerialization propertyListWithData:tempData options:NSPropertyListImmutable format:NULL error:NULL];
    
    return [returnStr stringByReplacingOccurrencesOfString:@"\\r\\n"withString:@"\n"];
}




// 不正规json 序列化
+ (NSDictionary *)josnSerialization:(NSDictionary *)json{
    if (json) {
        NSData *jsonData = [NSJSONSerialization dataWithJSONObject:json options:NSJSONWritingPrettyPrinted error:nil];
        NSDictionary *content = [NSJSONSerialization JSONObjectWithData:jsonData options:NSJSONReadingMutableContainers error:nil];//转换数据格式
        return content;
    }
    return nil;
}

//get object(NSArray、NSDictionary)from JSON.NSData  json序列化
+ (void)getObjectFromJSONData:(NSData *)data
                   complement:(void(^)(NSError *error, id jsonObject))complement{
    id jsonObject = nil;
    
    if (data.length != 0)
    {
        NSError *error = nil;
        jsonObject = [NSJSONSerialization JSONObjectWithData:data options:NSJSONReadingAllowFragments error:&error];
        if (!jsonObject)
        {
            NSString *strJSON = [[NSString alloc]initWithData:data encoding:NSUTF8StringEncoding];
            jsonObject = [VNHttpRequestManager replaceUnicode:strJSON];
            
#ifdef DEBUG
            NSLog(@"数据解析错误 -JSONValue failed. Error trace is: %@,\n JSON:%@", error,strJSON);
#endif
            
            if (!jsonObject) {
                complement(error,nil);
                return;
            }
            
        }
    }
    NSData *jsonData      = [NSJSONSerialization dataWithJSONObject:jsonObject
                                                            options:NSJSONWritingPrettyPrinted
                                                              error:nil];
    NSString *strResponse = [[NSString alloc] initWithData:jsonData encoding:NSUTF8StringEncoding];
    
    if ([strResponse containsString:@"\\u"]) {
        strResponse = [VNHttpRequestManager replaceUnicode:strResponse];
    }
    NSLog(@"Response Value = %@",strResponse );
    
    complement(nil,jsonObject);
}

//获取错误code对应状态
+ (void)getErrorInfo:(NSError * _Nonnull )error infoData:(ServerResponseInfo *)infoData{
    
    if (error.code == -1009) {
        infoData.errorMessage = @"无网络连接";
    }else if (error.code == -1001){
        infoData.errorMessage = @"请求超时";
    }else if (error.code == -1003){
        infoData.errorMessage = @"找不到主机";
    }else if (error.code == -1004){
        infoData.errorMessage = @"服务器没有启动";
    }else{
        infoData.errorMessage = @"其他错误";
    }
    
    if (infoData.httpCode == 200) {
        infoData.httpMessage = @"请求成功";
    }else if (infoData.httpCode > 200 && infoData.httpCode < 207){
        infoData.httpMessage = @"请求成功,服务器未响应";
    }else if(infoData.httpCode == 400){
        infoData.httpMessage = @"请求body参数有误";
    }else if(infoData.httpCode == 401){
        infoData.httpMessage = @"未授权,身份验证出问题";
    }else if(infoData.httpCode == 403){
        infoData.httpMessage = @"禁止访问";
    }else if(infoData.httpCode == 404){
        infoData.httpMessage = @"请求路径找不到";
    }else if(infoData.httpCode > 499 && infoData.httpCode < 506){
        infoData.httpMessage = @"服务器错误";
    }else{
        infoData.httpMessage = @"其他错误";
    }
    
    
}


+ (void)netWorkReachability:(void(^)(NSString *))currentStatus{
    AFNetworkReachabilityManager *manager = [AFNetworkReachabilityManager sharedManager];
    [manager startMonitoring];
    [manager setReachabilityStatusChangeBlock:^(AFNetworkReachabilityStatus status) {
        switch (status) {
            case AFNetworkReachabilityStatusUnknown:
            {
                //未知网络
                NSLog(@"未知网络");
                currentStatus(@"未知网络");
            }
                break;
            case AFNetworkReachabilityStatusNotReachable:
            {
                //无法联网
                currentStatus(@"无网络网");
                
            }
                break;
                
            case AFNetworkReachabilityStatusReachableViaWWAN:
            {
                //手机自带网络
                NSLog(@"当前使用的是2g/3g/4g网络");
                currentStatus(@"手机网络");
                
            }
                break;
            case AFNetworkReachabilityStatusReachableViaWiFi:
            {
                //WIFI
                NSLog(@"当前在WIFI网络下");
                currentStatus(@"当前在WIFI网络下");
                
            }
                
        }
    }];
}





@end

@implementation ServerResponseInfo


@end



//    NSURLErrorUnknown = -1,
//    NSURLErrorCancelled = -999,
//    NSURLErrorBadURL = -1000,
//    NSURLErrorTimedOut = -1001,
//    NSURLErrorUnsupportedURL = -1002,
//    NSURLErrorCannotFindHost = -1003,
//    NSURLErrorCannotConnectToHost = -1004,
//    NSURLErrorDataLengthExceedsMaximum = -1103,
//    NSURLErrorNetworkConnectionLost = -1005,
//    NSURLErrorDNSLookupFailed = -1006,
//    NSURLErrorHTTPTooManyRedirects = -1007,
//    NSURLErrorResourceUnavailable = -1008,
//    NSURLErrorNotConnectedToInternet = -1009,
//    NSURLErrorRedirectToNonExistentLocation = -1010,
//    NSURLErrorBadServerResponse = -1011,
//    NSURLErrorUserCancelledAuthentication = -1012,
//    NSURLErrorUserAuthenticationRequired = -1013,
//    NSURLErrorZeroByteResource = -1014,
//    NSURLErrorCannotDecodeRawData = -1015,
//    NSURLErrorCannotDecodeContentData = -1016,
//    NSURLErrorCannotParseResponse = -1017,
//    NSURLErrorInternationalRoamingOff = -1018,
//    NSURLErrorCallIsActive = -1019,
//    NSURLErrorDataNotAllowed = -1020,
//    NSURLErrorRequestBodyStreamExhausted = -1021,
//    NSURLErrorFileDoesNotExist = -1100,
//    NSURLErrorFileIsDirectory = -1101,
//    NSURLErrorNoPermissionsToReadFile = -1102,
//    NSURLErrorSecureConnectionFailed = -1200,
//    NSURLErrorServerCertificateHasBadDate = -1201,
//    NSURLErrorServerCertificateUntrusted = -1202,
//    NSURLErrorServerCertificateHasUnknownRoot = -1203,
//    NSURLErrorServerCertificateNotYetValid = -1204,
//    NSURLErrorClientCertificateRejected = -1205,
//    NSURLErrorClientCertificateRequired = -1206,
//    NSURLErrorCannotLoadFromNetwork = -2000,
//    NSURLErrorCannotCreateFile = -3000,
//    NSURLErrorCannotOpenFile = -3001,
//    NSURLErrorCannotCloseFile = -3002,
//    NSURLErrorCannotWriteToFile = -3003,
//    NSURLErrorCannotRemoveFile = -3004,
//    NSURLErrorCannotMoveFile = -3005,
//    NSURLErrorDownloadDecodingFailedMidStream = -3006,
//    NSURLErrorDownloadDecodingFailedToComplete = -3007

请求样例 POST 方式

[VNHttpRequestManager sendJSONRequestWithMethod:RequestMethod_Post pathUrl:@"https://wesus.api.cogniive.misoft.com/pid/v1.0/identification" params:@{@"locale":@"en-us"} complement:^(ServerResponseInfo *serverInfo) {
        if (serverInfo.isSuccess) {
            
        }
    }];

二、Swift AFRequestManager 封装类

此类基于 Alamofire 封装具体思想雷同,大家看用例

//  AFRequestManager.swift
//  SwiftGrammar
//
//  Created by guohq on 2018/7/20.
//  Copyright © 2018年 guohq. All rights reserved.
//


import UIKit
import Alamofire

public enum FILE_TYPE{
    case  FILE_TYPE_NORMAL        //
    case  FILE_TYPE_IMG           // 图片
    case  FILE_TYPE_VOICE_VIDEO   // 音频、视频
}

// MARK: - 下载状态
public enum DownloadStatus {
    case downloading //下载中
    case suspend     //暂停
    case complete    // 完成
}

fileprivate enum RequestType{
    case  RequestType_UPLOAD           // 上传文件
    case  RequestType_REQUEST          // 数据交互

}




// 超时时间设置
struct RequestTimeOut {
   static var timeOut:TimeInterval = 20
    // 结构体 属于 值类型  要在 实例 或 类 方法中修改成员变量 需要添加 mutating 关键字(结构体变量赋值是在初始化时进行的,之后想修改必须添加mutating)
}

// 超时时间设置  ---> 10
fileprivate var manager: Alamofire.SessionManager = {
    
    let configuration = URLSessionConfiguration.default
    configuration.timeoutIntervalForRequest = RequestTimeOut.timeOut
    configuration.httpAdditionalHeaders = SessionManager.defaultHTTPHeaders
    return Alamofire.SessionManager(configuration: configuration)
    
}();

class AFRequestManager: NSObject {
    
    /// 下载任务管理
    fileprivate var downloadTasks = [String: DownloadTaskManager]()

    // 单例
    private static let singleManager = AFRequestManager();
    // 私有化构造方法
    override init() {}
    // 公开类方法 获取单例类 默认超时时间20秒
    private class func defaultSingleManager() -> AFRequestManager{
        return singleManager
    }

    //  请求头设置
    private func requestHeaderSetting(header:[String:String]? = [:]) -> HTTPHeaders{
        
        var headers  : HTTPHeaders = [String: String]()
        headers["Ocp-Apim-Subscription-Key"] = "发送到发送到发送到发送到"
        headers["Accept"] = "application/json"
        
        for key in (header?.keys)!{
            headers[key] = header![key]
        }
        
        return headers;
    }
    
    
    /** 请求回调处理  */
    typealias AFSProgressBlock   = (Double) -> Void;
    
    typealias AFSComplementBlock = (AFSRequestSuccess?,AFSRequestErrorInfo?,Bool) -> Void;
    
    
    /**
     请求调用方法
     
     @param method           请求方法  get  post  put  delete.... 默认post
     @param requestUrl       请求地址
     @param parmas           请求体
     @param complement       回调方法
     */
    public class func sendRequestNet(method:HTTPMethod,
                                     requestUrl:URLConvertible,
                                     parmas:Parameters,
                                     headers:HTTPHeaders = SessionManager.defaultHTTPHeaders,
                                     complement:@escaping AFSComplementBlock){

        var headers  : HTTPHeaders = AFRequestManager.defaultSingleManager().requestHeaderSetting(header: headers)
        headers["Content-Type"] = "application/json;charset=utf-8"
        AFRequestManager().senderRequestHandle(method: method,
                                               requestUrl: requestUrl,
                                               parmas: parmas,
                                               headers:headers,
                                               complement: complement)
        
        
    }
    
 
    /**
     上传文件
     
     @param fileArr          文件list
     @param requestUrl       请求地址
     @param parmas           请求体
     @param fileType         文件类型 两类:图片  或者  视频、音频
     @param progressBlock    上传文件进度
     @param complement       回调方法
     */
    public class func uploadFielRequest(fileArr:[String],
                                        requestUrl:URLConvertible,
                                        param:Parameters,
                                        fileType:FILE_TYPE,
                                        headers:HTTPHeaders = SessionManager.defaultHTTPHeaders,
                                        progressBlock:@escaping AFSProgressBlock,
                                        complement:@escaping AFSComplementBlock){
        var headers  : HTTPHeaders = AFRequestManager.defaultSingleManager().requestHeaderSetting(header: headers)
        headers["Content-Type"] = "multipart/form-data"
        
        AFRequestManager().senderRequest(requestType: RequestType.RequestType_UPLOAD,
                                         fileArr: fileArr,
                                         requestUrl: requestUrl,
                                         param: param,
                                         headers:headers,
                                         fileType: fileType,
                                         progressBlock: progressBlock,
                                         complement: complement)
    }
    
    
    
    // 请求特殊处理
    private func senderRequestHandle(method:HTTPMethod,
                                     requestUrl:URLConvertible,
                                     parmas:Parameters,
                                     headers:HTTPHeaders,
                                     redirectCount:Int = 0,
                                     complement:@escaping AFSComplementBlock) {
        self.senderRequest(requestType: RequestType.RequestType_REQUEST,
                                         method: method,
                                         requestUrl: requestUrl,
                                         param: parmas,
                                         headers:headers,
                                         complement:{(response,errorInfo,statuCode) in
                                            // 请求重定向重复请求最多两次递归
                                            if errorInfo?.httpCode == 302 && redirectCount < 2{
                                                let redirectCount = 1 + redirectCount
                                                self.senderRequestHandle(method: method, requestUrl: requestUrl, parmas: parmas,headers:headers, redirectCount: redirectCount, complement: complement)
                                            }else{
                                                complement(response,errorInfo,statuCode)
                                            }
        })
    }
    
    
    /**
     @param requestType      请求类型 上传或者文件交互
     @param fileArr          文件list
     @param method           请求方法  get  post  put  delete.... 默认post
     @param requestUrl       请求地址
     @param parmas           请求体
     @param fileType         文件类型 两类:图片  或者  视频、音频
     @param progressBlock    上传文件进度
     @param complement       回调方法
     */
    private func senderRequest(requestType:RequestType,
                               method:HTTPMethod = .post,
                               fileArr:[String] = [],
                               requestUrl:URLConvertible,
                               param:Parameters = [:],
                               headers:HTTPHeaders = [:],
                               fileType :FILE_TYPE = FILE_TYPE.FILE_TYPE_NORMAL,
                               progressBlock :@escaping AFSProgressBlock = {_ in},
                               complement:@escaping AFSComplementBlock   = {_,_,_  in}) {
        
        
        let completionHandler  = self.completionHandler(param: param, complement: complement);
        let encodingCompletion = self.encodingCompletion(param: param, progressBlock:progressBlock,complement:complement)
        
        if requestType == RequestType.RequestType_REQUEST {
            // 发起请求
            AFRequestManager.defaultSingleManager().startReqest(method: method, requestUrl: requestUrl, parma: param, headers:headers, completionHandler: completionHandler)
        }else{
            // 上传文件
            AFRequestManager.defaultSingleManager().uploadFile(fileArr: fileArr, requestUrl: requestUrl, param: param, fileType: fileType, headers:headers, encodingCompletion: encodingCompletion)
        }
        
    }
    
    // 请求回调函数
    @discardableResult
    private func completionHandler(param:Parameters = [:],
                                   complement:@escaping AFSComplementBlock) -> (DataResponse<Any>) -> Void{
        let completionHandler:(DataResponse<Any>) -> Void = { (response) in
            print("请求路径:    ======  \(String(describing: response.request?.url?.absoluteString))")  // original url request
            print("参数   :    ======  \(param)")                        // 请求参数
            print("请求状态:    ======  \(response.result)")                    // 请求结果
            let str = String(data:response.data!, encoding: String.Encoding.utf8)!
            print("响应内容:    ======  \(str.unicodeStr)")
            // 请求处理
            self.handleResponserData(response: response,complement: complement);
        }
        return completionHandler
    }
    
    // 上传文件回调函数
    @discardableResult
    private func encodingCompletion(param:Parameters = [:],
                                    progressBlock :@escaping AFSProgressBlock = {_ in},
                                    complement:@escaping AFSComplementBlock)
        -> ((SessionManager.MultipartFormDataEncodingResult) -> Void)?{
        let encodingCompletion: ((SessionManager.MultipartFormDataEncodingResult) -> Void)? = {(encodingResult) in
            switch encodingResult {
            case .success(let upload, _, _):
                //获取上传进度
                upload.uploadProgress(queue: DispatchQueue.global(qos: .utility)) { progress in
                    progressBlock(progress.fractionCompleted);
                    print( progress.fractionCompleted)
                };
                upload.responseJSON { response in
                    print("请求路径:    ======  \(String(describing: response.request))")  // original url request
                    print("参数   :    ======  \(param)")                        // 请求参数
                    print("请求状态:    ======  \(response.result)")                    // 请求结果
                    let str = String(data:response.data!, encoding: String.Encoding.utf8)!
                    print("响应内容:    ======  \(str.unicodeStr)")
                    // 请求处理
                    self.handleResponserData(response: response, complement: complement);
                }
                
            case .failure(let encodingError):
                self.handleRequestErrorData(response: nil, error_data: encodingError as NSError, complement: complement)
            }
        }
        return encodingCompletion
    }
    
    
    /**
     向服务器发起请求
     
     @param encoding    请求编码类型 JSONEncoding、URLEncoding、PropertyListEncoding
     @param method      请求方法  get  post  put  delete....
     @param requestUrl  请求地址
     @param parmas      请求体
     */
    private func startReqest(method:HTTPMethod,
                             requestUrl:URLConvertible,
                             parma:Parameters,
                             headers:HTTPHeaders = [:],
                             completionHandler:@escaping (DataResponse<Any>) -> Void){
        
        
        manager.request(requestUrl, method: method, parameters: parma, encoding: JSONEncoding.default, headers: headers)           .validate()
            .responseJSON(completionHandler: completionHandler)
        
    }
    
    /**
     向服务器发起请求
     
     @param fileArr     文件list
     @param method      请求方法  get  post  put  delete....
     @param requestUrl  请求地址
     @param parmas      请求体
     @param encodingCompletion 上传回调
     */
    private func uploadFile(fileArr:[String],
                            requestUrl:URLConvertible,
                            param:Parameters,
                            fileType:FILE_TYPE,
                            headers:HTTPHeaders = [:],
                            encodingCompletion: ((SessionManager.MultipartFormDataEncodingResult) -> Void)?)  {
        
        let   multipartFormData :(MultipartFormData) -> Void = { (multipartFormData) in
            
            if fileType == FILE_TYPE.FILE_TYPE_IMG {
                self.uploadIMGFile(multipartFormData: multipartFormData,
                                   fileArr: fileArr)
            }else{
                self.uploadVideoVoiceFile(multipartFormData: multipartFormData,
                                          fileArr: fileArr)
            }
            
        }
        
        
        manager.upload(multipartFormData: multipartFormData, to: requestUrl,headers: headers,encodingCompletion:encodingCompletion)
        
    }
    
    /** 上传图片 */
    private func uploadIMGFile(multipartFormData:MultipartFormData,fileArr:[String]){
        let fileType:String = (fileArr.first?.components(separatedBy: ".").last)!
        
        for filePath:String in fileArr {
            
            let nowTime = self.getCurrentTime()
            let fileName = nowTime + fileType
            
            let image:UIImage = UIImage(contentsOfFile: filePath)!
            var imgData = UIImageJPEGRepresentation(image, 1)
            let scaleNum:Double = Double(300*1024.0 / Float(imgData!.count))
            if(scaleNum < 1){
                
                imgData = UIImageJPEGRepresentation(image, CGFloat(scaleNum))
            }else{
                
                imgData = UIImageJPEGRepresentation(image, 0.1)
            }
            multipartFormData.append(imgData!, withName: "image", fileName: fileName, mimeType: "jpg/png");
            
        }
    }
    
    /** 上传视频或者音频 */
    private func uploadVideoVoiceFile(multipartFormData:MultipartFormData,fileArr:[String]){
        
        let fileType:String = (fileArr.first?.components(separatedBy: ".").last)!
        
        for filePath:String in fileArr {
            
            let nowTime = self.getCurrentTime()
            let fileName = nowTime + fileType
            let data1 = NSData(contentsOfFile: filePath)! as Data // let mp4Path = URL(fileURLWithPath: filePath)
            multipartFormData.append(data1, withName: "file", fileName: fileName, mimeType: "map4/wav/amr");
            
        }
        
    }
    
    // 获取当前时间
    private func getCurrentTime() -> String{
        let date = NSDate()
        let timeFormatter = DateFormatter()
        timeFormatter.dateFormat = "yyyyMMddHHmmss"
        let strNowTime = timeFormatter.string(from: date as Date) as String
        return strNowTime
    }
    
    
    /** 响应数据处理 */
    private func handleResponserData(response:DataResponse<Any>,complement:AFSComplementBlock){
        
        if response.result.error != nil { // 服务器未返回数据
            self.handleRequestErrorData( response: response, error_data: response.result.error! as NSError,complement: complement);
        }else if let value = response.result.value { // 服务器又返回数据
            
            if (value as? NSDictionary) == nil && (value as? NSArray) == nil { // 返回格式不对
                self.handleRequestSuccessWithFaliedBlcokData(complement: complement)
            }else{
                
                self.handleRequestSuccessData(value: value, complement:complement);
                
            }
            
        }
        
    }
    
    /** 响应数据出错处理 */
    private func handleRequestErrorData(response:DataResponse<Any>?,error_data:NSError?,complement:AFSComplementBlock){
        
        var error = NSError()
        var errorInfo = AFSRequestErrorInfo()
        
        if response != nil{
            print("错误提示: \(String(describing: response!.error))")   // http url response
            print("响应信息: \(String(describing: response!.response))")
            if let code = response?.response?.statusCode{
                errorInfo.httpCode = code
            }
            if let er = response?.result.error as NSError?{
                error = er
            }
            
        }else{
            error = error_data!
            print(error)
            for item in error.userInfo {
                print("error ++++++ \(item)")
            }
        }
        
        errorInfo.code           = error.code;
        errorInfo.error          = error;
        if let head = response?.response?.allHeaderFields as? [String:String] {
            errorInfo.responseHeader = head
        }
        
        if ( errorInfo.code == -1009 ) {
            errorInfo.message = "无网络连接"
        }else if ( errorInfo.code == -1001 ){
            errorInfo.message = "请求超时"
        }else if ( errorInfo.code == -1005 ){
            errorInfo.message = "网络连接丢失(服务器忙)";
        }else if ( errorInfo.code == -1004 ){
            errorInfo.message = "服务器没有启动"
        }else{
            errorInfo.message = "其他错误"
        }
        
        errorInfo.httpMessage = HttpStatusCode.codeMessage(code: errorInfo.httpCode)
        
        complement(nil,errorInfo,false)
        
    }
    
    /** 响应数据解析出问题 */
    private func handleRequestSuccessData(value:Any!,complement:AFSComplementBlock){
        
        var success   = AFSRequestSuccess();

        
        if let result = value as? NSArray{
            success.responseArr = result
        }else{
            success.responseDic = value as? NSDictionary
        }
        complement(success ,nil,true);
        
    }
    
    
    
    /** 服务器返回数据解析出错*/
    
    private func handleRequestSuccessWithFaliedBlcokData(complement:AFSComplementBlock){
        
        var errorInfo   = AFSRequestErrorInfo();
        
        errorInfo.code  = -1;
        
        errorInfo.message = "数据解析出错";
        
        complement(nil,errorInfo,false);
        
    }
    
    
    /**
     向服务器发起请求
     
     @param encoding    请求编码类型 JSONEncoding、URLEncoding、PropertyListEncoding
     @param method      请求方法  get  post  put  delete....
     @param requestUrl  请求地址
     @param parmas      请求体
     @param destination 下载文件地址
     */
    @discardableResult
    public class  func downloadFile(   method:HTTPMethod = .get,
                                 requestUrl:URLConvertible,
                                 parma:Parameters = [:],
                                 headers:HTTPHeaders = SessionManager.defaultHTTPHeaders,
                                 fileName:String,
                                 downProgress:@escaping (Double) -> Void,
                                 completion: @escaping (Alamofire.Result<String>,String?)->()) -> DownloadTaskManager{
        
        
        let headers  : HTTPHeaders = AFRequestManager.defaultSingleManager().requestHeaderSetting(header:headers)
        let downloadManager =  DownloadTaskManager().download(requestUrl, method: method, parameters: parma, headers: headers, fileName:fileName)
        downloadManager.downloadProgress(progress: downProgress).response(completion: completion)
        
        AFRequestManager.defaultSingleManager().downloadTasks[requestUrl as! String] = downloadManager
        
        downloadManager.downCompletion = { (urlPath) in
            deleteResumeData(urlPath: urlPath)
        }
        return downloadManager
        
    }
    
    public class func getDownloadStatus(pathUrl:String) -> DownloadStatus{
        let taskRequest = AFRequestManager.defaultSingleManager().downloadTasks[pathUrl]
        return taskRequest?.downloadStatus ?? .suspend
    }
    
    public class func downloadCancle(pathUrl:String){
        let taskRequest = AFRequestManager.defaultSingleManager().downloadTasks[pathUrl]
        taskRequest?.downloadCancle()
    }
    //
    public class func downloadFilePath(pathUrl:String) -> String{
        let taskRequest = AFRequestManager.defaultSingleManager().downloadTasks[pathUrl]
        return taskRequest?.downFilePath ?? "not found filepath"
    }
    
}

/** 访问出错具体原因 */

struct AFSRequestErrorInfo {
    
    var code          = 0           // code 值
    var httpCode      = 0           // http 响应状态码
    
    var httpMessage   = ""          // http 请求提示
    var message       = ""          // 请求提示
    
    var error         = NSError();  // 错误信息
    var responseHeader:[String:String]?         // 请求响应header
}

/** 响应数据 */
struct AFSRequestSuccess {
    var responseDic:NSDictionary?
    var responseArr:NSArray    =   NSArray()
}


fileprivate enum  HttpMessage:String{
    case  success           =   "成功"
    case  paramError        =   "请求body参数有误"
    case  urlError          =   "请求路径找不到"
    case  accessUnnormal    =   "未授权,身份验证出问题"
    case  binaryAccess      =   "禁止访问"
    case  serviceError      =   "服务器错误"
    case  successRequest    =   "请求成功,服务器未响应"
    case  unnormal          =   "其他错误"
    
}

/** http 响应码对应状态 */
fileprivate struct HttpStatusCode  {
    
    static  func codeMessage(code:Int) -> String {
        var message:String = ""
        
        switch code {
        case 200:
            message = HttpMessage.success.rawValue
            break
        case 201,202,203,204,205,206:
            message = HttpMessage.successRequest.rawValue
            break
        case 400:
            message = HttpMessage.paramError.rawValue
            break
        case 401:
            message = HttpMessage.accessUnnormal.rawValue
            break
        case 403:
            message = HttpMessage.binaryAccess.rawValue
            break
        case 404:
            message = HttpMessage.urlError.rawValue
            break
        case 500,501,502,503,504,505:
            message = HttpMessage.serviceError.rawValue
            break
        default:
            message = HttpMessage.unnormal.rawValue
            break
        }
        
        return message
    }
    
}


// MARK: - taskManager
public class DownloadTaskManager {
    
    fileprivate var downloadRequest:  DownloadRequest?
    fileprivate var downloadStatus:   DownloadStatus = .suspend
    fileprivate var downCompletion:   ((String)->())?
    fileprivate var downUrl:          String?
    fileprivate var downFilePath:     String?

    
    /** 初始化 --- 生成key

      *  Parameters:
      *   - url: url
      *   - parameters: 参数
      *   - dynamicParams: 变化的参数,例如 时间戳-token 等
      */
    @discardableResult
    fileprivate func download(
        _ url: URLConvertible,
        method: HTTPMethod = .get,
        parameters: Parameters? = nil,
        encoding: ParameterEncoding = URLEncoding.default,
        headers: HTTPHeaders? = nil,
        fileName: String?)
        -> DownloadTaskManager
    {
        let destination = downloadDestination(fileName)
       
        downUrl = url as? String
        let result = getResumeData(urlPath: downUrl!)

        if let resumData = result{
            downloadRequest = manager.download(resumingWith:resumData , to: destination)
        }else{
            downloadRequest = manager.download(url, method: method, parameters: parameters, encoding: encoding, headers: headers, to: destination)
        }
        
        downloadStatus = .downloading
        return self
    }
    
    fileprivate func downloadCancle(){
           downloadRequest?.cancel()
    }
    
    /// 下载进度
    @discardableResult
    fileprivate func downloadProgress(progress: @escaping ((Double) -> Void)) -> DownloadTaskManager {
        downloadRequest?.downloadProgress(closure: { (pro) in
            progress(pro.fractionCompleted)
         })
        return self
    }
    /// 响应
    fileprivate func response(completion: @escaping (Alamofire.Result<String>,String?)->()) {
        downloadRequest?.responseData(completionHandler: {[weak self] (response) in
            switch response.result {
            case .success:
                print("响应者状态 \(response)")
                self?.downloadStatus = .complete
                let str = response.destinationURL?.absoluteString
                completion(Alamofire.Result.success(str!),self?.downFilePath)
                self?.downCompletion!(str!)
            case .failure(let error):
                print("下载出错  \(error)")
                self?.downloadStatus = .suspend
                saveResumeData(pathUrl: (self?.downUrl!)!, resumData: response.resumeData! )
                completion(Alamofire.Result.failure(error),self?.downFilePath)
            }
        })
    }
    /**
    下载文件位置
 
     * - Parameter fileName: 自定义文件名
     * - Returns: 下载位置
     */
    private func downloadDestination(_ fileName: String?) -> DownloadRequest.DownloadFileDestination {
        let destination: DownloadRequest.DownloadFileDestination = {[weak self] _, response in
            let cachesURL = FileManager.default.urls(for: .cachesDirectory, in: .userDomainMask)[0]
            if let fileName = fileName {
                let fileURL = cachesURL.appendingPathComponent(fileName)
                self?.downFilePath = fileURL.absoluteString
                return (fileURL, [.removePreviousFile, .createIntermediateDirectories])
            } else {
                let fileURL = cachesURL.appendingPathComponent(response.suggestedFilename!)
                self?.downFilePath = fileURL.absoluteString
                return (fileURL, [.removePreviousFile, .createIntermediateDirectories])
            }
        }
        return destination
    }
    
    
}

// 保存切片数据
fileprivate func saveResumeData(pathUrl:String,resumData:Data? = nil){
    let info:UserDefaults = UserDefaults.standard
    
    if let resumData = resumData{
        info.set(resumData, forKey: pathUrl)
    }
}

// 获取切片数据
fileprivate func getResumeData(urlPath:String) -> Data?{
    let info:UserDefaults = UserDefaults.standard
    return info.object(forKey: urlPath) as? Data
}

// 清空数据
fileprivate func deleteResumeData(urlPath:String){
    let info:UserDefaults = UserDefaults.standard
    info.removeObject(forKey: urlPath)
}

// Unicode 编码转码
extension String{
    var unicodeStr:String{
        let str1:String =  self.replacingOccurrences(of: "\\u", with: "\\U")
        let str2 = str1.replacingOccurrences(of: "\"", with: "\\\"")
        let str3:String = "\"" + str2 + "\""
        let tempData = str3.data(using: String.Encoding.utf8)
        var returnStr = ""
        do{
            returnStr = try PropertyListSerialization.propertyList(from: tempData!, options:PropertyListSerialization.MutabilityOptions(rawValue: String.Encoding.utf8.rawValue), format: nil) as! String
        }catch{
            print(error)
            
        }
        return returnStr.replacingOccurrences(of: "\\r\\n", with: "\n")
    }
}

用例

post

  AFRequestManager.sendRequestNet(method: .post, requestUrl: "http://app.u17.com/v3/appV3_3/ios/phone/comic/boutiqueListNew", parmas: ["sexType":"1","key":"fabe6953ce6a1b8738bd2cabebf893a472d2b6274ef7ef6f6a5dc7171e5cafb14933ae65c70bceb97e0e9d47af6324d50394ba70c1bb462e0ed18b88b26095a82be87bc9eddf8e548a2a3859274b25bd0ecfce13e81f8317cfafa822d8ee486fe2c43e7acd93e9f19fdae5c628266dc4762060f6026c5ca83e865844fc6beea59822ed4a70f5288c25edb1367700ebf5c78a27f5cce53036f1dac4a776588cd890cd54f9e5a7adcaeec340c7a69cd986:::open","target":"U17_3.0","version":"3.3.3","v":"3320101","model":"Simulator","device_id":"29B09615-E478-4320-8E6A-55B1DE48CB36","time":Int32(Date().timeIntervalSince1970)] ) { (response, errorInfo, resultStatus) in
            if resultStatus{
                print("===")
            }
        }

断点下载

let downFile = "http://audio.xmcdn.com/group11/M01/93/AF/wKgDa1dzzJLBL0gCAPUzeJqK84Y539.m4a"
        let downloadStatus = AFRequestManager.getDownloadStatus(pathUrl: downFile)

        switch downloadStatus {
        case .complete:
            let filePath = AFRequestManager.downloadFilePath(pathUrl:downFile)
            print("文件路径 ===== \(filePath)")
            break
        case .suspend:
            AFRequestManager.downloadFile(requestUrl: downFile, headers:["123":"guohong"],fileName: "gdsfa.mp4", downProgress: { (progress) in
                print("\(progress)")

            }) { (resultStr,filePath)  in
                print("结果字符串 \(resultStr)")
            }
            break
        case .downloading:
            AFRequestManager.downloadCancle(pathUrl: downFile)
            break
        }

上传文件

  let request:String = "https://westus.api.cognitive.microsoft.com/spid/v1.0/identificationProfiles/12312312/enroll?shortAudio=true"
        AFRequestManager.uploadFielRequest(fileArr: ["/Users/用户名/Desktop/文件本地路径], requestUrl: request, param: [:], fileType: FILE_TYPE.FILE_TYPE_VOICE_VIDEO,headers:[:], progressBlock: { (progress) in
            
        }) { (response, errorInfo, resultStatus) in
            print("=======")
        }

通过 RequestTimeOut.timeOut = 10 来设置超时时间

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 199,440评论 5 467
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 83,814评论 2 376
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 146,427评论 0 330
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 53,710评论 1 270
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 62,625评论 5 359
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,014评论 1 275
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,511评论 3 390
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,162评论 0 254
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,311评论 1 294
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,262评论 2 317
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,278评论 1 328
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,989评论 3 316
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,583评论 3 303
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,664评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,904评论 1 255
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,274评论 2 345
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 41,856评论 2 339

推荐阅读更多精彩内容

  • Android 自定义View的各种姿势1 Activity的显示之ViewRootImpl详解 Activity...
    passiontim阅读 171,259评论 25 707
  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 134,561评论 18 139
  • 这一天晚上,虽然又是月色娇好,可他们却在星月全无的地下人间大酒店,地下第18层、名叫人间地狱的包间里坐了下来。他们...
    成运阅读 463评论 0 9
  • 笑对人生_af67阅读 169评论 0 0
  • 日记星球269号星宝宝,我正在参加日记星球第十二期蜕变之旅,这是我的第0313原创日记。我相信日积月累的力量! 今...
    cf302fb8f796阅读 409评论 0 0