接口请求的本地缓存策略
本文是在 AFNetworking 的基础上,加入了本地缓存策略,并对请求错误,进行了定制,以统一前端对用户的错误提示。
缓存策略
包含3种策略,详见以下缓存策略的定义说明。
/**
接口缓存策略
*/
typedef NS_ENUM(NSUInteger, TTVRequestCachePolicy) {
//1、先查询本地缓存
//2.1、有缓存则直接返回
//2.2、无缓存则请求服务器后返回,并更新本地缓存
TTVRequestCachePolicyNormal,
//不使用缓存,直接请求服务器
TTVRequestCachePolicyOnline,
//1、先查询本地缓存。
//2.1、有缓存则返回,然后请求服务器再次返回,并更新本地缓存。
//2.2、无缓存则联网请求服务器返回最新数据,并更新本地缓存。
TTVRequestCachePolicyHybrid,
};
策略对应的业务需求
TTVRequestCachePolicyNormal
只取本地缓存的数据返回。同时在后台请求最新的数据,并更新缓存。
对应的业务需求是,一些更新周期非常长的数据,可以使用此种策略。
TTVRequestCachePolicyOnline
只请求最新的网络数据。
对应一些不需要使用到本地缓存的业务需求。
TTVRequestCachePolicyHybrid
先返回本地缓存数据,再返回最新的网络数据。
因为App端有一种通用的需求,就是在没有网络或网络较差时,需要先展示本地缓存的数据,同时请求最新数据,请求成功后,将获取到的最新数据更新到UI上。
请求成功或失败的回调block
请求成功的处理
/**
请求的成功回调
@param responseObject 返回的正确数据
@param isCache 是否缓存
*/
typedef void (^TTVRequestSuccessBlock)(id _Nullable responseObject, BOOL isCache);
指定的返回的数据,以及 isCache
代表是否来自本地缓存
请求失败的处理
/**
请求的失败回调
@param errCode 错误码
@param errMessage 错误消息
*/
typedef void (^TTVRequestFailureBlock)(NSInteger errCode, NSString *_Nullable errMessage);
请求失败的回调block,指定了错误码和错误提示语,在调用请求接口时,可直接使用并展示给用户。
在请求过程中,加入了网络错误的判断,直接返回指定的网络错误提示语,并中断请求。
有待优化
对于本地缓存策略,需要加入缓存有效期。
完整代码示例
/**
GET或者POST请求
@param isGetOrPost YES:GET请求,NO:POST请求
@param URLString 请求的URL
@param parameters 请求参数
@param cachePolicy 缓存策略
@param cacheKey 缓存key,命名方式:模块名+id+userId
@param downloadProgressBlock 请求成功下载加载进度回调Block
@param successBlock 请求成功调用Block,Block中的responseObject参数
@param failureBlock 请求失败调用Block
@return
*/
+ (nonnull NSURLSessionDataTask *)request:(BOOL)isGetOrPost
url:(nonnull NSString *)URLString
parameters:(nullable id)parameters
cachePolicy:(TTVRequestCachePolicy)cachePolicy
cacheKey:(NSString *_Nullable)cacheKey
progress:(void (^ _Nullable)(NSProgress * _Nonnull progress))downloadProgressBlock
success:(TTVRequestSuccessBlock)successBlock
failure:(TTVRequestFailureBlock)failureBlock
{
TTVLogVerbose(@"GET Request with Cache>>>>>>>>>> [%@][%@]", URLString, parameters);
AFHTTPSessionManager *manager = [TTVNetworkManager ttv_addHeaderForSignWithRequestURL:URLString mathod:isGetOrPost ? @"GET" : @"POST" parameters:parameters needToDecrypt:YES];
__block YYCache *yyCache = [YYCache cacheWithName:@"TTVRequestCache"];
//缓存key,加上版本号,用于升级接口后的数据更新
__block NSString *cacheKeyFinal = [NSString stringWithFormat:@"%@+%@", cacheKey, [[self class] getServerAPIVesion:URLString]];
//成功回调处理
void (^doSuccessBlock)(id, BOOL) = ^(id responseObject, BOOL isCache) {
TTVLogVerbose(@"GET Request with Cache Success<<<<<<<<<<[ %@ ]\n isCache:%@\n responseObject: %@ \n\n", URLString, @(isCache), responseObject);
if (successBlock) {
successBlock(responseObject, isCache);
if (!isCache) {
[self checkResponseObject:responseObject];
}
}
};
//失败回调处理
void (^doFailureBlock)(NSError *, TTVRequestFailureBlock) = ^(NSError *error, TTVRequestFailureBlock failureBlock) {
TTVLogWarn(@"GET Request with Cache Failure<<<<<<<<<<[ %@ ]\n%@\n\n", URLString, error);
//返回具体的错误提示
if (failureBlock) {
TTVNetWorkError *netWorkError = [TTVNetWorkError ttv_errorWithError:error userInfo:error.userInfo];
NSInteger errorCode = 0;
if (error) {
errorCode = [error.userInfo[@"body"][@"errorCode"] integerValue];
}
failureBlock(errorCode, netWorkError.ttv_tips);
}
};
//请求
void (^requestBlock)(BOOL) = ^(BOOL isGetOrPost)
{
/********************************
无网络时,直接返回错误消息
*/
AFNetworkReachabilityStatus status = [AFNetworkReachabilityManager sharedManager].networkReachabilityStatus;
if (status == AFNetworkReachabilityStatusNotReachable) {
if (failureBlock) {
failureBlock(TTVRequestErrorCodeNoNetwork, TTVErrorMessageNoNetwork);
}
return;
}
/********************************
网络正常时,继续请求
*/
if (isGetOrPost) {
[manager GET:URLString
parameters:parameters
progress:nil
success:^(NSURLSessionDataTask * _Nonnull task, id _Nullable responseObject)
{
doSuccessBlock(responseObject, NO);
[yyCache setObject:responseObject forKey:cacheKeyFinal];
}
failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error)
{
doFailureBlock(error, failureBlock);
}];
} else {
[manager POST:URLString
parameters:parameters
progress:nil
success:^(NSURLSessionDataTask * _Nonnull task, id _Nullable responseObject)
{
doSuccessBlock(responseObject, NO);
[yyCache setObject:responseObject forKey:cacheKeyFinal];
}
failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error)
{
doFailureBlock(error, failureBlock);
}];
}
};
/********************************
处理数据,缓存逻辑
*/
if (cachePolicy == TTVRequestCachePolicyNormal) {
id cacheData = [yyCache objectForKey:cacheKeyFinal];
if (cacheData) {
//返回缓存
doSuccessBlock(cacheData, YES);
} else {
//请求服务器
requestBlock(isGetOrPost);
}
} else if (cachePolicy == TTVRequestCachePolicyOnline) {
//请求服务器
requestBlock(isGetOrPost);
} else if (cachePolicy == TTVRequestCachePolicyHybrid) {
//返回缓存
id cacheData = [yyCache objectForKey:cacheKeyFinal];
if (cacheData) {
doSuccessBlock(cacheData, YES);
}
//请求服务器,再返回一次
requestBlock(isGetOrPost);
}
//返回空
return nil;
}