封装AFN3.0网络请求框架,使用FMDB缓存并可带有时效性缓存

现在项目处于维护阶段,以前的一部分代码随着用户量的增加,不断暴露出问题,特别是网络请求--回调处理方面的修改比较多。每次修改对应的接口都要跳到对应控制器啊,或者是搜一发提示语,其他网络请求操作也是分散在各个控制器内,乱七八糟。好烦的。趁着有点空闲,研究了下别人的封装,改进了项目中使用的封装,并做成了SDK发布在cocoaPods上,欢迎使用.
pod 'TYNetworkTool'即可使用。

项目原先的AFN封装

也就是很简单的按照AFN封装了一下,.m
//
//  PPRHttpManager.m
//  PaopaoRunning
//
//  Created by 王天永 on 16/5/22.

//

#import "PPRHttpManager.h"
#import 
#import "NSString+Paths.h"
#import "NSString+UUID.h"

@implementation PPRHttpManager
+ (NSURLSessionDataTask *)get:(NSString *)url params:(NSDictionary *)params success:(void (^)(id responseObject))success failure:(void (^)(NSError *error))failure{
    
    AFHTTPSessionManager *manager = [AFHTTPSessionManager manager];
    manager.requestSerializer = [AFHTTPRequestSerializer serializer];
    manager.responseSerializer = [AFHTTPResponseSerializer serializer];
    if (IOS11) {    //适配IOS11增加的这句
        manager.securityPolicy.allowInvalidCertificates = YES;
    }
    manager.requestSerializer.timeoutInterval = 15;
//    [manager.requestSerializer setValue:@"application/x-www-form-urlencoded; charset=utf-8" forHTTPHeaderField:@"Content-Type"];
    NSURLSessionDataTask *task = [manager GET:url parameters:params progress:nil success:^(NSURLSessionDataTask * _Nonnull task, id  _Nullable responseObject) {
        if (success){
            NSString *responseString = [[NSString alloc]initWithData:responseObject encoding:NSUTF8StringEncoding];
            PRLog(@"responseString---%@",responseString);
            NSDictionary *dict = [NSJSONSerialization JSONObjectWithData:responseObject options:NSJSONReadingAllowFragments error:nil];
            success(dict);
        }
    } failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {
        if (failure){
            failure(error);
        }
    }];
    return task;
}

+ (NSURLSessionDataTask *)post:(NSString *)url params:(NSDictionary *)params success:(void (^)(id responseObject))success failure:(void (^)(NSError *error))failure{
    AFHTTPSessionManager *manager = [AFHTTPSessionManager manager];
    manager.requestSerializer = [AFHTTPRequestSerializer serializer];
    manager.responseSerializer = [AFHTTPResponseSerializer serializer];
//    manager.securityPolicy.allowInvalidCertificates = YES;
    manager.requestSerializer.timeoutInterval = 15;
    NSURLSessionDataTask *task = [manager POST:url parameters:params progress:nil success:^(NSURLSessionDataTask * _Nonnull task, id  _Nullable responseObject) {
        if (success){
            NSString *responseString = [[NSString alloc]initWithData:responseObject encoding:NSUTF8StringEncoding];
            PRLog(@"responseString---%@",responseString);
            
            NSDictionary *dict = [NSJSONSerialization JSONObjectWithData:responseObject options:NSJSONReadingAllowFragments error:nil];

            success(dict);
        }
    } failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {
        if (failure){

            failure(error);
        }
    }];
    return task;

}

+ (NSURLSessionDownloadTask *)downloadFile:(NSString *)url progress:(void (^)(CGFloat progress))progress success:(void (^)(id responseObject))success failure:(void (^)(NSError *error))failure{

    NSURLSessionConfiguration *configuration = [NSURLSessionConfiguration defaultSessionConfiguration];
    AFURLSessionManager *manager = [[AFURLSessionManager alloc] initWithSessionConfiguration:configuration];
    
    NSURL *URL = [NSURL URLWithString:url];
    NSURLRequest *request = [NSURLRequest requestWithURL:URL];
    NSURLSessionDownloadTask *downloadTask = [manager downloadTaskWithRequest:request progress:^(NSProgress *downloadProgress){
//    NSLog(@"progress is %f", downloadProgress.fractionCompleted);
        progress(downloadProgress.fractionCompleted);
    } destination:^NSURL *(NSURL *targetPath, NSURLResponse *response) {
        NSString *caches = [NSString cachesPath];
        NSString *videovideoCaches = [caches stringByAppendingPathComponent:@"videoCaches"];
        
        NSURL *videoFileUrl = [NSURL fileURLWithPath:videovideoCaches];
        return [videoFileUrl URLByAppendingPathComponent:[response suggestedFilename] ];
    } completionHandler:^(NSURLResponse *response, NSURL *filePath, NSError *error) {
        if (!error){
            PRLog(@"%@",F(@"File downloaded to: %@", filePath));
            success(filePath);
            
            
        }else{
            PRLog(@"File download fail!");
            failure(error);
        }
    }];
    [downloadTask resume];
    return downloadTask;
    
}

@end

着手封装新的低耦合的AFN网络封装

封装的主要方向

  1. 本地缓存优化,请求可根据需求(缓存的时效性等)决定是否直接使用本地缓存,或者请求到新数据后刷新本地。(当前已完成)
  2. 增加短时缓存,避免相同的请求重复调用,浪费用户流量。(当前已完成)
  3. 封装请求失败的重试机制。
  4. 将成功失败提示的逻辑封装。
  5. 其他比如网络状况监控,请求一键撤销等。(当前已完成)
    感谢巨巨文章提供思路和参考
    猿题库YTKNetwork的GitHub
    iOS 工作中封装通用性网络请求框架——鸿雁长飞光不度
    AFNetworking3.x与YYCache的二次封装,和FMDB说拜拜——jkpang

封装的结构考虑

  1. 考虑以后换网络框架的可能。(网络单独封装
  2. 考虑以后换回调处理及提示的可能。网络请求和回调处理要低耦合,能够单独抽离。(回调和显示的单独封装
  3. 考虑以后换缓存框架的可能。(缓存单独封装

封装的结构

考虑到网络请求的通用性,而回调处理和提示等并不通用,所以回调的封装暂时不纳入SDK,但是仍然建议将网络请求的回调封装起来存于一处,相同回调调用相应的统一方法,以后需要改动改这一处就够了,如本人GitHub上的TYNetworkManage文件夹内所封装的方式。
所以最终的SDK内的文件结构是这样的(暂时

封装AFN3.0网络请求框架,使用FMDB缓存并可带有时效性缓存_第1张图片
TYNetworkTool封装结构

· 第一层 网络请求入口 TYNetworkTool,.h文件对外暴露,用户之间调用即可完成网络请求。.m调用 TYCacheTool.h的方法来完成缓存的存储和读取;
· 第二层 缓存的存取入口 TYCacheTool,主要来完成缓存的业务逻辑
· 第三层 FMDB的实际缓存操作,实际操控SQLite文件来进行存取操作。

文件内容
//
//  TYNetworkTool.h
//  TYNetworkHelper
//
//  Created by 王天永 on 2017/7/14.
//  Copyright © 2017年 王天永. All rights reserved.
//

#import 
#import 
#import "TYCacheTool.h"

typedef NS_ENUM(NSUInteger, TYNetworkStatusType) {
    /// 未知网络
    TYNetworkStatusUnknown,
    /// 无网络
    TYNetworkStatusNotReachable,
    /// 手机网络
    TYNetworkStatusReachableViaWWAN,
    /// WIFI网络
    TYNetworkStatusReachableViaWiFi
};

typedef NS_ENUM(NSUInteger, TYRequestSerializer) {
    /// 设置请求数据为JSON格式
    TYRequestSerializerJSON,
    /// 设置请求数据为二进制格式
    TYRequestSerializerHTTP,
};

typedef NS_ENUM(NSUInteger, TYResponseSerializer) {
    /// 设置响应数据为JSON格式
    TYResponseSerializerJSON,
    /// 设置响应数据为二进制格式
    TYResponseSerializerHTTP,
};

/// 请求成功的Block
typedef void(^TYHttpRequestSuccess)(id responseObject);

/// 请求失败的Block
typedef void(^TYHttpRequestFailed)(NSError *error);

/// 缓存的Block
typedef void(^TYHttpRequestCache)(id responseCache);

/// 上传或者下载的进度, Progress.completedUnitCount:当前大小 - Progress.totalUnitCount:总大小
typedef void (^TYHttTYrogress)(NSProgress *progress);

/// 网络状态的Block
typedef void(^TYNetworkStatus)(TYNetworkStatusType status);

@class AFHTTPSessionManager;
@interface TYNetworkTool : NSObject

/// 有网YES, 无网:NO
+ (BOOL)isNetwork;

/// 手机网络:YES, 反之:NO
+ (BOOL)isWWANNetwork;

/// WiFi网络:YES, 反之:NO
+ (BOOL)isWiFiNetwork;

/// 取消所有HTTP请求
+ (void)cancelAllRequest;

/// 实时获取网络状态,通过Block回调实时获取(此方法可多次调用)
+ (void)networkStatusWithBlock:(TYNetworkStatus)networkStatus;

/// 取消指定URL的HTTP请求
+ (void)cancelRequestWithURL:(NSString *)URL;

/// 开启日志打印 (Debug级别)
+ (void)openLog;

/// 关闭日志打印,默认关闭
+ (void)closeLog;


/**
 *  GET请求,无缓存
 *
 *  @param URL        请求地址
 *  @param parameters 请求参数
 *  @param success    请求成功的回调
 *  @param failure    请求失败的回调
 *
 *  @return 返回的对象可取消请求,调用cancel方法
 */
+ (__kindof NSURLSessionTask *)GET:(NSString *)URL
                        parameters:(id)parameters
                           success:(TYHttpRequestSuccess)success
                           failure:(TYHttpRequestFailed)failure;

/**
 *  GET请求,自动缓存
 *
 *  @param URL           请求地址
 *  @param parameters    请求参数
 *  @param responseCache 缓存数据的回调
 *  @param success       请求成功的回调
 *  @param failure       请求失败的回调
 *
 *  @return 返回的对象可取消请求,调用cancel方法
 */
+ (__kindof NSURLSessionTask *)GET:(NSString *)URL
                        parameters:(id)parameters
                     responseCache:(TYHttpRequestCache)responseCache
                           success:(TYHttpRequestSuccess)success
                           failure:(TYHttpRequestFailed)failure;

/**
 GET请求,带时效自动缓存

 @param URL 请求地址
 @param parameters 请求参数
 @param life 缓存时效
 @param responseCache 缓存数据的回调
 @param success 请求成功的回调
 @param failure 请求失败的回调
 @return 返回的对象可取消请求,调用cancel方法
 */
+ (__kindof NSURLSessionTask *)GET:(NSString *)URL
                        parameters:(id)parameters
                   userfulLifeUnit:(TYTimeUnit)timeUnit
                      userfullLife:(double)life
                     responseCache:(TYHttpRequestCache)responseCache
                           success:(TYHttpRequestSuccess)success
                           failure:(TYHttpRequestFailed)failure;
/**
 *  POST请求,无缓存
 *
 *  @param URL        请求地址``
 *  @param parameters 请求参数
 *  @param success    请求成功的回调
 *  @param failure    请求失败的回调
 *
 *  @return 返回的对象可取消请求,调用cancel方法
 */
+ (__kindof NSURLSessionTask *)POST:(NSString *)URL
                         parameters:(id)parameters
                            success:(TYHttpRequestSuccess)success
                            failure:(TYHttpRequestFailed)failure;

/**
 *  POST请求,自动缓存
 *
 *  @param URL           请求地址
 *  @param parameters    请求参数
 *  @param responseCache 缓存数据的回调
 *  @param success       请求成功的回调
 *  @param failure       请求失败的回调
 *
 *  @return 返回的对象可取消请求,调用cancel方法
 */
+ (__kindof NSURLSessionTask *)POST:(NSString *)URL
                         parameters:(id)parameters
                      responseCache:(TYHttpRequestCache)responseCache
                            success:(TYHttpRequestSuccess)success
                            failure:(TYHttpRequestFailed)failure;

/**
 GET请求,带时效自动缓存
 
 @param URL 请求地址
 @param parameters 请求参数
 @param life 缓存时效
 @param responseCache 缓存数据的回调
 @param success 请求成功的回调
 @param failure 请求失败的回调
 @return 返回的对象可取消请求,调用cancel方法
 */
+ (__kindof NSURLSessionTask *)POST:(NSString *)URL
                        parameters:(id)parameters
                   userfulLifeUnit:(TYTimeUnit)timeUnit
                      userfullLife:(double)life
                     responseCache:(TYHttpRequestCache)responseCache
                           success:(TYHttpRequestSuccess)success
                           failure:(TYHttpRequestFailed)failure;

/**
 *  上传文件
 *
 *  @param URL        请求地址
 *  @param parameters 请求参数
 *  @param name       文件对应服务器上的字段
 *  @param filePath   文件本地的沙盒路径
 *  @param progress   上传进度信息
 *  @param success    请求成功的回调
 *  @param failure    请求失败的回调
 *
 *  @return 返回的对象可取消请求,调用cancel方法
 */
+ (__kindof NSURLSessionTask *)uploadFileWithURL:(NSString *)URL
                                      parameters:(id)parameters
                                            name:(NSString *)name
                                        filePath:(NSString *)filePath
                                        progress:(TYHttTYrogress)progress
                                         success:(TYHttpRequestSuccess)success
                                         failure:(TYHttpRequestFailed)failure;

/**
 *  上传单/多张图片
 *
 *  @param URL        请求地址
 *  @param parameters 请求参数
 *  @param name       图片对应服务器上的字段
 *  @param images     图片数组
 *  @param fileNames  图片文件名数组, 可以为nil, 数组内的文件名默认为当前日期时间"yyyyMMddHHmmss"
 *  @param imageScale 图片文件压缩比 范围 (0.f ~ 1.f)
 *  @param imageType  图片文件的类型,例:png、jpg(默认类型)....
 *  @param progress   上传进度信息
 *  @param success    请求成功的回调
 *  @param failure    请求失败的回调
 *
 *  @return 返回的对象可取消请求,调用cancel方法
 */
+ (__kindof NSURLSessionTask *)uploadImagesWithURL:(NSString *)URL
                                        parameters:(id)parameters
                                              name:(NSString *)name
                                            images:(NSArray *)images
                                         fileNames:(NSArray *)fileNames
                                        imageScale:(CGFloat)imageScale
                                         imageType:(NSString *)imageType
                                          progress:(TYHttTYrogress)progress
                                           success:(TYHttpRequestSuccess)success
                                           failure:(TYHttpRequestFailed)failure;

/**
 *  下载文件
 *
 *  @param URL      请求地址
 *  @param fileDir  文件存储目录(默认存储目录为Download)
 *  @param progress 文件下载的进度信息
 *  @param success  下载成功的回调(回调参数filePath:文件的路径)
 *  @param failure  下载失败的回调
 *
 *  @return 返回NSURLSessionDownloadTask实例,可用于暂停继续,暂停调用suspend方法,开始下载调用resume方法
 */
+ (__kindof NSURLSessionTask *)downloadWithURL:(NSString *)URL
                                       fileDir:(NSString *)fileDir
                                      progress:(TYHttTYrogress)progress
                                       success:(void(^)(NSString *filePath))success
                                       failure:(TYHttpRequestFailed)failure;


#pragma mark - 设置AFHTTPSessionManager相关属性
#pragma mark 注意: 因为全局只有一个AFHTTPSessionManager实例,所以以下设置方式全局生效
/**
 在开发中,如果以下的设置方式不满足项目的需求,就调用此方法获取AFHTTPSessionManager实例进行自定义设置
 (注意: 调用此方法时在要导入AFNetworking.h头文件,否则可能会报找不到AFHTTPSessionManager的❌)
 @param sessionManager AFHTTPSessionManager的实例
 */
+ (void)setAFHTTPSessionManagerProperty:(void(^)(AFHTTPSessionManager *sessionManager))sessionManager;

/**
 *  设置网络请求参数的格式:默认为二进制格式
 *
 *  @param requestSerializer TYRequestSerializerJSON(JSON格式),TYRequestSerializerHTTP(二进制格式),
 */
+ (void)setRequestSerializer:(TYRequestSerializer)requestSerializer;

/**
 *  设置服务器响应数据格式:默认为JSON格式
 *
 *  @param responseSerializer TYResponseSerializerJSON(JSON格式),TYResponseSerializerHTTP(二进制格式)
 */
+ (void)setResponseSerializer:(TYResponseSerializer)responseSerializer;

/**
 *  设置请求超时时间:默认为30S
 *
 *  @param time 时长
 */
+ (void)setRequestTimeoutInterval:(NSTimeInterval)time;

/// 设置请求头
+ (void)setValue:(NSString *)value forHTTPHeaderField:(NSString *)field;

/**
 *  是否打开网络状态转圈菊花:默认打开
 *
 *  @param open YES(打开), NO(关闭)
 */
+ (void)openNetworkActivityIndicator:(BOOL)open;

/**
 配置自建证书的Https请求, 参考链接: http://blog.csdn.net/syg90178aw/article/details/52839103
 
 @param cerPath 自建Https证书的路径
 @param validatesDomainName 是否需要验证域名,默认为YES. 如果证书的域名与请求的域名不一致,需设置为NO; 即服务器使用其他可信任机构颁发
 的证书,也可以建立连接,这个非常危险, 建议打开.validatesDomainName=NO, 主要用于这种情况:客户端请求的是子域名, 而证书上的是另外
 一个域名。因为SSL证书上的域名是独立的,假如证书上注册的域名是www.google.com, 那么mail.google.com是无法验证通过的.
 */
+ (void)setSecurityPolicyWithCerPath:(NSString *)cerPath validatesDomainName:(BOOL)validatesDomainName;

+ (NSString *)jsonToString:(id)data;
@end

//
//  TYCacheTool.h
//  TYNetworkTool
//
//  Created by 王天永 on 2017/7/19.
//  Copyright © 2017年 王天永. All rights reserved.
//

#import 


typedef NS_ENUM (NSUInteger,TYTimeUnit) {
    TYNone,
    TYSecond,
    TYMinute,
    TYHour,
    TYDay,
};

@interface TYCacheTool : NSObject

/**
 无失效日期缓存

 @param httpData 缓存数据
 @param url 请求url
 @param parameters 请求参数
 */
+ (void)setHttpCache:(id)httpData URL:(NSString *)url parameters:(NSDictionary *)parameters;
/**
 带时效的缓存

 @param httpData 缓存数据
 @param url 请求url
 @param parameters 请求参数
 @param timeUnit 时间单位,TYNone则无时效
 @param life 时效数值,具体单位与timeUnit有关,最小单位 秒
 */
+ (void)setHttpCache:(id)httpData URL:(NSString *)url parameters:(NSDictionary *)parameters userfulLifeUnit:(TYTimeUnit)timeUnit life:(double)life;
/**
 获取缓存数据
 
 @param url 请求url
 @param parameter 请求参数
 @return 缓存数据
 */
+ (id)httpCacheWithURL:(NSString *)url parameters:(NSDictionary *)parameter;

@end

//
//  TYFMDBTool.h
//  TYNetworkTool
//
//  Created by 王天永 on 2017/7/20.
//  Copyright © 2017年 王天永. All rights reserved.
//

#import 
@class TYCacheTool;


@interface TYFMDBTool : NSObject


/**
 创建tool实例,包含一个dmdb实例对象

 @param name 对应账号的唯一标识符
 @return 返回fmdbtool实例
 */
+ (instancetype)fmdbWithName:(NSString *)name;

/**
 创建表单

 @param tableName 表单名
 @return FMDB实例
 */
- (instancetype)createTableWithName:(NSString *)tableName;


/**
 储存缓存请求来的回调数据(无时效)
 
 @param httpData 回调数据
 @param cacheKey 存储的关键字
 */
- (void)setHttpCache:(id)httpData forCacheKey:(NSString *)cacheKey;

/**
 带时效的缓存
 
 @param httpData 缓存数据
 @param cacheKey 存储的关键字
 @param life 时效数值,具体单位与timeUnit有关
 */
- (void)setHttpCache:(id)httpData userfulLife:(double)life forCacheKey:(NSString *)cacheKey;


/**
 获取本地缓存

 @param cacheKey 存储的关键字
 @return 缓存本地的网络请求
 */
- (id)httpCacheForCacheKey:(NSString *)cacheKey;
@end

感觉带时效的缓存还有改进的余地,后续再继续改进吧,以新增方法的方式。(当前的方式适合这样的需求:如果是有效的缓存,先读取了显示在界面上,等数据获取到了再显示获取到的数据。但是这样就没有达到节省流量的目的了,需要再SDK内增加先取缓存,无有效缓存再发送网络请求的方法
本人的这个SDK已经发布在cocoapods上,pod 'TYNetworkTool'即可使用。

你可能感兴趣的:(封装AFN3.0网络请求框架,使用FMDB缓存并可带有时效性缓存)