1.网络请求

第一种实现方式:

  功能:GET POST 请求

缓存逻辑:

  1.是否要刷新本地缓存,不需要就直接发起无缓存的网络请求,否则直接读取本地数据

  2.需要刷新本地缓存,先读取本地数据,有就返回,没有就发起缓存的网络请求

  3.无网络时直接读取本地缓存

#import "AFHTTPSessionManager.h"

/**
*该类默认只要导入头文件就会自动检测网络状态,且会在没有网络和未知网络的时候,自动从本地数据库中读取缓存。
*数据库网络缓存是基于猿题库公司对FMDB进行封装的轻量级 key-value 存储框架
*详情请见 https://github.com/yuantiku/YTKKeyValueStore
*对该类如有疑问可以拉个issues
*/
@interface JMHttpRequestMethod : AFHTTPSessionManager typedef NS_ENUM(NSUInteger, JMRequestSerializer) {
JMRequestSerializerJSON, // 设置请求数据为JSON格式
JMRequestSerializerPlainText // 设置请求数据为普通 text/html
}; typedef NS_ENUM(NSUInteger, JMResponseSerializer) {
JMResponseSerializerJSON, // 设置响应数据为JSON格式
JMResponseSerializerHTTP, // 设置响应数据为二进制格式
JMResponseSerializerXML // 设置响应数据为XML格式
}; #pragma mark - 程序入口设置网络请求头API 一般调用一次即可 /**
设置 请求和响应类型和超时时间
@param requestType 默认为请求类型为JSON格式
@param responseType 默认响应格式为JSON格式
@param timeOut 请求超时时间 默认为20秒
*/
+(void)setTimeOutWithTime:(NSTimeInterval)timeOut
requestType:(JMRequestSerializer)requestType
responseType:(JMResponseSerializer)responseType; /**
设置 请求头
@param httpBody 根据服务器要求 配置相应的请求体
*/
+ (void)setHttpBodyWithDic:(NSDictionary *)httpBody; #pragma mark - 网络工具 API
/**
获取当前的网络状态 @return YES 有网 NO 没有联网
*/
+(BOOL)getCurrentNetWorkStatus; /**
获取网络缓存 文件大小
@return size 单位M 默认保留两位小数 如: 0.12M
*/
+ (NSString *)fileSizeWithDBPath;
/**
清除所有网络缓存
*/
+ (void)cleanNetWorkRefreshCache; #pragma mark - GET 请求API /**
GET 请求 不用传参 API
@param url 请求的url
@param refreshCache 是否对该页面进行缓存
@param success 请求成功回调
@param fail 请求失败回调
@return self
*/
+ (JMHttpRequestMethod *)getWithUrl:(NSString *)url
refreshCache:(BOOL)refreshCache
success:(void(^)(id responseObject))success
fail:(void(^)(NSError *error))fail; /**
GET 请求 传参数的API
@param url 请求的url
@param refreshCache 是否对该页面进行缓存
@param params 请求数据向服务器传的参数
@param success 请求成功回调
@param fail 请求失败回调
@return self
*/
+ (JMHttpRequestMethod *)getWithUrl:(NSString *)url
refreshCache:(BOOL)refreshCache
params:(NSDictionary *)params
success:(void(^)(id responseObject))success
fail:(void(^)(NSError *error))fail; /**
GET 请求 带有进度回调的 API
@param url 请求的url
@param refreshCache 是否对该页面进行缓存
@param params 请求数据向服务器传的参数
@param progress 请求进度回调
@param success 请求成功回调
@param fail 请求失败回调
@return self
*/
+ (JMHttpRequestMethod *)getWithUrl:(NSString *)url
refreshCache:(BOOL)refreshCache
params:(NSDictionary *)params
progress:(void(^)(int64_t bytesRead, int64_t totalBytesRead))progress
success:(void(^)(id responseObject))success
fail:(void(^)(NSError *error))fail; #pragma mark - POST 请求API /**
POST 请求API
@param url 请求的url
@param refreshCache 是否对该页面进行缓存
@param params 请求数据向服务器传的参数
@param success 请求成功回调
@param fail 请求失败回调
@return self
*/
+ (JMHttpRequestMethod *)postWithUrl:(NSString *)url
refreshCache:(BOOL)refreshCache
params:(NSDictionary *)params
success:(void(^)(id responseObject))success
fail:(void(^)(NSError *error))fail; /**
POST 请求 带有进度回调的 API @param url 请求的url
@param refreshCache 是否对该页面进行缓存
@param params 请求数据向服务器传的参数
@param progress 请求进度回调
@param success 请求成功回调
@param fail 请求失败回调 @return self
*/
+ (JMHttpRequestMethod *)postWithUrl:(NSString *)url
refreshCache:(BOOL)refreshCache
params:(NSDictionary *)params
progress:(void(^)(int64_t bytesRead, int64_t totalBytesRead))progress
success:(void(^)(id responseObject))success
fail:(void(^)(NSError *error))fail; @end
#import "JMHttpRequestMethod.h"
#import "YTKKeyValueStore.h" #define PATH_OF_NetWork [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) objectAtIndex:0] typedef NS_ENUM(NSUInteger, JMNetworkStatus) {
JMNetworkStatusUnknown, //未知的网络
JMNetworkStatusNotNetWork, //没有网络
JMNetworkStatusReachableViaWWAN,//手机蜂窝数据网络
JMNetworkStatusReachableViaWiFi //WIFI 网络
}; @interface JMHttpRequestMethod ()
@end @implementation JMHttpRequestMethod static NSString *const httpCache = @"NetworkCache";
static YTKKeyValueStore *_store;
static JMNetworkStatus _status;
static BOOL _isHasNetWork; + (void)load
{
JMHttpRequestMethod *httpMethod;
httpMethod.requestSerializer = [AFJSONRequestSerializer serializer];
//设置请求的超时时间
httpMethod.requestSerializer.timeoutInterval = .f;
//设置服务器返回结果的类型:JSON (AFJSONResponseSerializer,AFHTTPResponseSerializer)
httpMethod.responseSerializer = [AFJSONResponseSerializer serializer]; httpMethod.responseSerializer.acceptableContentTypes = [NSSet setWithObjects:
@"application/json",
@"text/html",
@"text/json",
@"text/plain",
@"text/javascript",
@"text/xml", @"image/*", nil];
[self startMonitoringNetworkStatus];
} /**
监测网络状态 (在程序入口,调用一次即可)
*/
+ (void)startMonitoringNetworkStatus
{
AFNetworkReachabilityManager *manager = [AFNetworkReachabilityManager sharedManager];
[manager setReachabilityStatusChangeBlock:^(AFNetworkReachabilityStatus status) {
switch (status)
{
case AFNetworkReachabilityStatusUnknown:
_isHasNetWork = NO;
_status = JMNetworkStatusUnknown;
break;
case AFNetworkReachabilityStatusNotReachable:
_isHasNetWork = NO;
_status = JMNetworkStatusNotNetWork;
NSLog(@"没有网的状态");
break;
case AFNetworkReachabilityStatusReachableViaWWAN:
_isHasNetWork = YES;
_status = JMNetworkStatusReachableViaWWAN;
break;
case AFNetworkReachabilityStatusReachableViaWiFi:
_isHasNetWork = YES;
_status = JMNetworkStatusReachableViaWiFi;
NSLog(@"现在是有网状态");
break;
}
}];
[manager startMonitoring];
}
/**
设置 请求和响应类型和超时时间 @param requestType 默认为请求类型为JSON格式
@param responseType 默认响应格式为JSON格式
@param timeOut 请求超时时间 默认为20秒
*/
+(void)setTimeOutWithTime:(NSTimeInterval)timeOut
requestType:(JMRequestSerializer)requestType
responseType:(JMResponseSerializer)responseType
{ JMHttpRequestMethod *httpMethod;
httpMethod.requestSerializer.timeoutInterval = timeOut;
switch (requestType) {
case JMRequestSerializerJSON:
httpMethod.requestSerializer = [AFJSONRequestSerializer serializer];
break;
case JMRequestSerializerPlainText:
httpMethod.requestSerializer = [AFHTTPRequestSerializer serializer];
break;
default:
break;
}
switch (responseType) {
case JMResponseSerializerJSON:
httpMethod.responseSerializer = [AFJSONResponseSerializer serializer];
break;
case JMResponseSerializerHTTP:
httpMethod.responseSerializer = [AFHTTPResponseSerializer serializer];
break;
case JMResponseSerializerXML:
httpMethod.responseSerializer = [AFXMLParserResponseSerializer serializer];
default:
break;
}
}
/**
设置 请求头 @param httpBody 根据服务器要求 配置相应的请求体
*/
+ (void)setHttpBodyWithDic:(NSDictionary *)httpBody
{
JMHttpRequestMethod *httpMethod;
for (NSString *key in httpBody.allKeys) {
if (httpBody[key] != nil) {
[httpMethod.requestSerializer setValue:httpBody[key] forHTTPHeaderField:key];
}
}
} /**
获取当前的网络状态 @return YES 有网 NO 没有联网
*/
+(BOOL)getCurrentNetWorkStatus
{
return _isHasNetWork;
} /**
获取网络缓存 文件大小 @return size 单位M
*/
+ (NSString *)fileSizeWithDBPath
{
NSFileManager* manager = [NSFileManager defaultManager];
if ([manager fileExistsAtPath:[PATH_OF_NetWork stringByAppendingPathComponent:httpCache]]){
unsigned long long fileSize = [[manager attributesOfItemAtPath:[PATH_OF_NetWork stringByAppendingPathComponent:httpCache] error:nil] fileSize];
NSString *size = [NSString stringWithFormat:@"%.2fM",fileSize/1024.0/1024.0];
return size;
}else {
return @"0M";
}
return ;
} /**
清除所有网络缓存
*/
+ (void)cleanNetWorkRefreshCache
{
NSError *error;
BOOL isSuccess = [[NSFileManager defaultManager]removeItemAtPath:[PATH_OF_NetWork stringByAppendingPathComponent:httpCache] error:&error];
if (isSuccess) {
NSLog(@"clean cache file is success");
}else {
if ([PATH_OF_NetWork stringByAppendingPathComponent:httpCache]) {
NSLog(@"error:%@",error.description);
}else {
NSLog(@"error: cache file is not exist");
} }
} #pragma mark - /**************GET 请求API ******************/ + (JMHttpRequestMethod *)getWithUrl:(NSString *)url
refreshCache:(BOOL)refreshCache
success:(void(^)(id responseObject))success
fail:(void(^)(NSError *error))fail
{
return [self getWithUrl:url refreshCache:refreshCache params:nil success:success fail:fail];
}
// 多一个params参数
+ (JMHttpRequestMethod *)getWithUrl:(NSString *)url
refreshCache:(BOOL)refreshCache
params:(NSDictionary *)params
success:(void(^)(id responseObject))success
fail:(void(^)(NSError *error))fail
{
return [self getWithUrl:url refreshCache:refreshCache params:params progress:nil success:success fail:fail];
}
// 多一个带进度回调
+ (JMHttpRequestMethod *)getWithUrl:(NSString *)url
refreshCache:(BOOL)refreshCache
params:(NSDictionary *)params
progress:(void(^)(int64_t bytesRead, int64_t totalBytesRead))progress
success:(void(^)(id responseObject))success
fail:(void(^)(NSError *error))fail
{
_store = [[YTKKeyValueStore alloc] initDBWithName:httpCache];
[_store createTableWithName:httpCache];
JMHttpRequestMethod *request = nil;
if ([JMHttpRequestMethod getCurrentNetWorkStatus]) {
if (!refreshCache) {
[self requestNotCacheWithHttpMethod: url:url params:params progress:progress success:success fail:fail];
}else {
NSDictionary *dict = [_store getObjectById:url fromTable:httpCache];
if (dict) {
success(dict);
}else {
[[self manager] GET:url parameters:params progress:^(NSProgress * _Nonnull downloadProgress) {
progress(downloadProgress.completedUnitCount, downloadProgress.totalUnitCount);
} success:^(NSURLSessionDataTask * _Nonnull task, id _Nullable responseObject) {
[_store putObject:responseObject withId:url intoTable:httpCache];
success(responseObject);
NSLog(@"\nRequest success, URL: %@\n params:%@\n response:%@\n\n",url,params,responseObject);
} failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {
fail(error);
NSLog(@"error = %@",error.description);
}];
}
}
} else {
NSDictionary *dict = [_store getObjectById:url fromTable:httpCache];
if (dict) {
success(dict);
}else {
NSLog(@"当前为无网络状态,本地也没有缓存数据");
}
}
return request;
}
#pragma mark - /*********************** POST 请求API **********************/ + (JMHttpRequestMethod *)postWithUrl:(NSString *)url
refreshCache:(BOOL)refreshCache
params:(NSDictionary *)params
success:(void(^)(id responseObject))success
fail:(void(^)(NSError *error))fail
{
return [self postWithUrl:url refreshCache:refreshCache params:params progress:nil success:success fail:fail];
} + (JMHttpRequestMethod *)postWithUrl:(NSString *)url
refreshCache:(BOOL)refreshCache
params:(NSDictionary *)params
progress:(void(^)(int64_t bytesRead, int64_t totalBytesRead))progress
success:(void(^)(id responseObject))success
fail:(void(^)(NSError *error))fail
{
JMHttpRequestMethod *request = nil;
if ([JMHttpRequestMethod getCurrentNetWorkStatus]) {
if (!refreshCache) {
[self requestNotCacheWithHttpMethod: url:url params:params progress:progress success:success fail:fail];
}else {
NSDictionary *dict = [_store getObjectById:url fromTable:httpCache];
if (dict) {
success(dict);
}else {
[[self manager] POST:url parameters:params progress:^(NSProgress * _Nonnull downloadProgress) {
progress(downloadProgress.completedUnitCount, downloadProgress.totalUnitCount);
} success:^(NSURLSessionDataTask * _Nonnull task, id _Nullable responseObject) {
[_store putObject:responseObject withId:url intoTable:httpCache];
success(responseObject);
NSLog(@"\nRequest success, URL: %@\n params:%@\n response:%@\n\n",url,params,responseObject);
} failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {
fail(error);
}];
}
}
} else {
NSDictionary *dict = [_store getObjectById:url fromTable:httpCache];
if (dict) {
success(dict);
}else {
NSLog(@"当前为无网络状态,本地也没有缓存数据");
}
} return request;
}
+ (void)requestNotCacheWithHttpMethod:(NSInteger)httpMethod
url:(NSString *)url
params:(NSDictionary *)params
progress:(void(^)(int64_t bytesRead, int64_t totalBytesRead))progress
success:(void(^)(id responseObject))success
fail:(void(^)(NSError *error))fail
{
if (httpMethod == ) {
[[self manager]GET:url parameters:params progress:^(NSProgress * _Nonnull downloadProgress) {
progress(downloadProgress.completedUnitCount, downloadProgress.totalUnitCount);
} success:^(NSURLSessionDataTask * _Nonnull task, id _Nullable responseObject) {
success(responseObject);
NSLog(@"\nRequest success, URL: %@\n params:%@\n response:%@\n\n",url,params,responseObject);
} failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {
fail(error);
}];
}else {
[[self manager ]POST:url parameters:params progress:^(NSProgress * _Nonnull uploadProgress) {
progress(uploadProgress.completedUnitCount, uploadProgress.totalUnitCount);
} success:^(NSURLSessionDataTask * _Nonnull task, id _Nullable responseObject) {
success(responseObject);
NSLog(@"\nRequest success, URL: %@\n params:%@\n response:%@\n\n",url,params,responseObject);
} failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {
fail(error);
}];
}
} @end

2.数据缓存(FMDB)

#import <Foundation/Foundation.h>

@interface YTKKeyValueItem : NSObject

@property (strong, nonatomic) NSString *itemId;
@property (strong, nonatomic) id itemObject;
@property (strong, nonatomic) NSDate *createdTime; @end @interface YTKKeyValueStore : NSObject - (id)initDBWithName:(NSString *)dbName; - (id)initWithDBWithPath:(NSString *)dbPath; - (void)createTableWithName:(NSString *)tableName; - (void)clearTable:(NSString *)tableName; - (void)close; ///************************ Put&Get methods ***************************************** - (void)putObject:(id)object withId:(NSString *)objectId intoTable:(NSString *)tableName; - (id)getObjectById:(NSString *)objectId fromTable:(NSString *)tableName; - (YTKKeyValueItem *)getYTKKeyValueItemById:(NSString *)objectId fromTable:(NSString *)tableName; - (void)putString:(NSString *)string withId:(NSString *)stringId intoTable:(NSString *)tableName; - (NSString *)getStringById:(NSString *)stringId fromTable:(NSString *)tableName; - (void)putNumber:(NSNumber *)number withId:(NSString *)numberId intoTable:(NSString *)tableName; - (NSNumber *)getNumberById:(NSString *)numberId fromTable:(NSString *)tableName; - (NSArray *)getAllItemsFromTable:(NSString *)tableName; - (void)deleteObjectById:(NSString *)objectId fromTable:(NSString *)tableName; - (void)deleteObjectsByIdArray:(NSArray *)objectIdArray fromTable:(NSString *)tableName; - (void)deleteObjectsByIdPrefix:(NSString *)objectIdPrefix fromTable:(NSString *)tableName; @end
#import "YTKKeyValueStore.h"
#import "FMDatabase.h"
#import "FMDatabaseQueue.h" #ifdef DEBUG
#define debugLog(s, ...) NSLog( @"[%@ in line %d] ➡️➡️➡️%@", [[NSString stringWithUTF8String:__FILE__] lastPathComponent], __LINE__, [NSString stringWithFormat:(s), ##__VA_ARGS__] )
#define debugMethod() NSLog(@"%s", __func__)
#define debugError(s, ...) NSLog( @"[%@ in line %d] ➡️➡️➡️%@", [[NSString stringWithUTF8String:__FILE__] lastPathComponent], __LINE__, [NSString stringWithFormat:(s), ##__VA_ARGS__] )
#else
#define debugLog(s,...)
#define debugMethod()
#define debugError(s, ...)
#endif #define PATH_OF_DOCUMENT [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) objectAtIndex:0] @implementation YTKKeyValueItem - (NSString *)description {
return [NSString stringWithFormat:@"id=%@, value=%@, timeStamp=%@", _itemId, _itemObject, _createdTime];
} @end @interface YTKKeyValueStore() @property (strong, nonatomic) FMDatabaseQueue * dbQueue; @end @implementation YTKKeyValueStore static NSString *const DEFAULT_DB_NAME = @"database.sqlite"; static NSString *const CREATE_TABLE_SQL =
@"CREATE TABLE IF NOT EXISTS %@ ( \
id TEXT NOT NULL, \
json TEXT NOT NULL, \
createdTime TEXT NOT NULL, \
PRIMARY KEY(id)) \
"; static NSString *const UPDATE_ITEM_SQL = @"REPLACE INTO %@ (id, json, createdTime) values (?, ?, ?)"; static NSString *const QUERY_ITEM_SQL = @"SELECT json, createdTime from %@ where id = ? Limit 1"; static NSString *const SELECT_ALL_SQL = @"SELECT * from %@"; static NSString *const CLEAR_ALL_SQL = @"DELETE from %@"; static NSString *const DELETE_ITEM_SQL = @"DELETE from %@ where id = ?"; static NSString *const DELETE_ITEMS_SQL = @"DELETE from %@ where id in ( %@ )"; static NSString *const DELETE_ITEMS_WITH_PREFIX_SQL = @"DELETE from %@ where id like ? "; + (BOOL)checkTableName:(NSString *)tableName {
if (tableName == nil || tableName.length == || [tableName rangeOfString:@" "].location != NSNotFound) {
debugLog(@"ERROR, table name: %@ format error.", tableName);
return NO;
}
return YES;
} - (id)init {
return [self initDBWithName:DEFAULT_DB_NAME];
} - (id)initDBWithName:(NSString *)dbName {
self = [super init];
if (self) {
NSString * dbPath = [PATH_OF_DOCUMENT stringByAppendingPathComponent:dbName];
debugLog(@"dbPath = %@", dbPath);
if (_dbQueue) {
[self close];
}
_dbQueue = [FMDatabaseQueue databaseQueueWithPath:dbPath];
}
return self;
} - (id)initWithDBWithPath:(NSString *)dbPath {
self = [super init];
if (self) {
debugLog(@"dbPath = %@", dbPath);
if (_dbQueue) {
[self close];
}
_dbQueue = [FMDatabaseQueue databaseQueueWithPath:dbPath];
}
return self;
} - (void)createTableWithName:(NSString *)tableName {
if ([YTKKeyValueStore checkTableName:tableName] == NO) {
return;
}
NSString * sql = [NSString stringWithFormat:CREATE_TABLE_SQL, tableName];
__block BOOL result;
[_dbQueue inDatabase:^(FMDatabase *db) {
result = [db executeUpdate:sql];
}];
if (!result) {
debugLog(@"ERROR, failed to create table: %@", tableName);
}
} - (void)clearTable:(NSString *)tableName {
if ([YTKKeyValueStore checkTableName:tableName] == NO) {
return;
}
NSString * sql = [NSString stringWithFormat:CLEAR_ALL_SQL, tableName];
__block BOOL result;
[_dbQueue inDatabase:^(FMDatabase *db) {
result = [db executeUpdate:sql];
}];
if (!result) {
debugLog(@"ERROR, failed to clear table: %@", tableName);
}
} - (void)putObject:(id)object withId:(NSString *)objectId intoTable:(NSString *)tableName {
if ([YTKKeyValueStore checkTableName:tableName] == NO) {
return;
}
NSError * error;
NSData * data = [NSJSONSerialization dataWithJSONObject:object options: error:&error];
if (error) {
debugLog(@"ERROR, faild to get json data");
return;
}
NSString * jsonString = [[NSString alloc] initWithData:data encoding:(NSUTF8StringEncoding)];
NSDate * createdTime = [NSDate date];
NSString * sql = [NSString stringWithFormat:UPDATE_ITEM_SQL, tableName];
__block BOOL result;
[_dbQueue inDatabase:^(FMDatabase *db) {
result = [db executeUpdate:sql, objectId, jsonString, createdTime];
}];
if (!result) {
debugLog(@"ERROR, failed to insert/replace into table: %@", tableName);
}
} - (id)getObjectById:(NSString *)objectId fromTable:(NSString *)tableName {
YTKKeyValueItem * item = [self getYTKKeyValueItemById:objectId fromTable:tableName];
if (item) {
return item.itemObject;
} else {
return nil;
}
} - (YTKKeyValueItem *)getYTKKeyValueItemById:(NSString *)objectId fromTable:(NSString *)tableName {
if ([YTKKeyValueStore checkTableName:tableName] == NO) {
return nil;
}
NSString * sql = [NSString stringWithFormat:QUERY_ITEM_SQL, tableName];
__block NSString * json = nil;
__block NSDate * createdTime = nil;
[_dbQueue inDatabase:^(FMDatabase *db) {
FMResultSet * rs = [db executeQuery:sql, objectId];
if ([rs next]) {
json = [rs stringForColumn:@"json"];
createdTime = [rs dateForColumn:@"createdTime"];
}
[rs close];
}];
if (json) {
NSError * error;
id result = [NSJSONSerialization JSONObjectWithData:[json dataUsingEncoding:NSUTF8StringEncoding]
options:(NSJSONReadingAllowFragments) error:&error];
if (error) {
debugLog(@"ERROR, faild to prase to json");
return nil;
}
YTKKeyValueItem * item = [[YTKKeyValueItem alloc] init];
item.itemId = objectId;
item.itemObject = result;
item.createdTime = createdTime;
return item;
} else {
return nil;
}
} - (void)putString:(NSString *)string withId:(NSString *)stringId intoTable:(NSString *)tableName {
if (string == nil) {
debugLog(@"error, string is nil");
return;
}
[self putObject:@[string] withId:stringId intoTable:tableName];
} - (NSString *)getStringById:(NSString *)stringId fromTable:(NSString *)tableName {
NSArray * array = [self getObjectById:stringId fromTable:tableName];
if (array && [array isKindOfClass:[NSArray class]]) {
return array[];
}
return nil;
} - (void)putNumber:(NSNumber *)number withId:(NSString *)numberId intoTable:(NSString *)tableName {
if (number == nil) {
debugLog(@"error, number is nil");
return;
}
[self putObject:@[number] withId:numberId intoTable:tableName];
} - (NSNumber *)getNumberById:(NSString *)numberId fromTable:(NSString *)tableName {
NSArray * array = [self getObjectById:numberId fromTable:tableName];
if (array && [array isKindOfClass:[NSArray class]]) {
return array[];
}
return nil;
} - (NSArray *)getAllItemsFromTable:(NSString *)tableName {
if ([YTKKeyValueStore checkTableName:tableName] == NO) {
return nil;
}
NSString * sql = [NSString stringWithFormat:SELECT_ALL_SQL, tableName];
__block NSMutableArray * result = [NSMutableArray array];
[_dbQueue inDatabase:^(FMDatabase *db) {
FMResultSet * rs = [db executeQuery:sql];
while ([rs next]) {
YTKKeyValueItem * item = [[YTKKeyValueItem alloc] init];
item.itemId = [rs stringForColumn:@"id"];
item.itemObject = [rs stringForColumn:@"json"];
item.createdTime = [rs dateForColumn:@"createdTime"];
[result addObject:item];
}
[rs close];
}];
// parse json string to object
NSError * error;
for (YTKKeyValueItem * item in result) {
error = nil;
id object = [NSJSONSerialization JSONObjectWithData:[item.itemObject dataUsingEncoding:NSUTF8StringEncoding]
options:(NSJSONReadingAllowFragments) error:&error];
if (error) {
debugLog(@"ERROR, faild to prase to json.");
} else {
item.itemObject = object;
}
}
return result;
} - (void)deleteObjectById:(NSString *)objectId fromTable:(NSString *)tableName {
if ([YTKKeyValueStore checkTableName:tableName] == NO) {
return;
}
NSString * sql = [NSString stringWithFormat:DELETE_ITEM_SQL, tableName];
__block BOOL result;
[_dbQueue inDatabase:^(FMDatabase *db) {
result = [db executeUpdate:sql, objectId];
}];
if (!result) {
debugLog(@"ERROR, failed to delete item from table: %@", tableName);
}
} - (void)deleteObjectsByIdArray:(NSArray *)objectIdArray fromTable:(NSString *)tableName {
if ([YTKKeyValueStore checkTableName:tableName] == NO) {
return;
}
NSMutableString *stringBuilder = [NSMutableString string];
for (id objectId in objectIdArray) {
NSString *item = [NSString stringWithFormat:@" '%@' ", objectId];
if (stringBuilder.length == ) {
[stringBuilder appendString:item];
} else {
[stringBuilder appendString:@","];
[stringBuilder appendString:item];
}
}
NSString *sql = [NSString stringWithFormat:DELETE_ITEMS_SQL, tableName, stringBuilder];
__block BOOL result;
[_dbQueue inDatabase:^(FMDatabase *db) {
result = [db executeUpdate:sql];
}];
if (!result) {
debugLog(@"ERROR, failed to delete items by ids from table: %@", tableName);
}
} - (void)deleteObjectsByIdPrefix:(NSString *)objectIdPrefix fromTable:(NSString *)tableName {
if ([YTKKeyValueStore checkTableName:tableName] == NO) {
return;
}
NSString *sql = [NSString stringWithFormat:DELETE_ITEMS_WITH_PREFIX_SQL, tableName];
NSString *prefixArgument = [NSString stringWithFormat:@"%@%%", objectIdPrefix];
__block BOOL result;
[_dbQueue inDatabase:^(FMDatabase *db) {
result = [db executeUpdate:sql, prefixArgument];
}];
if (!result) {
debugLog(@"ERROR, failed to delete items by id prefix from table: %@", tableName);
}
} - (void)close {
[_dbQueue close];
_dbQueue = nil;
} @end

3.使用示例

[JMHttpRequestMethod getWithUrl:url1 refreshCache:YES success:^(id responseObject) {

        NSData *jsonData = [NSJSONSerialization dataWithJSONObject:responseObject options:NSJSONWritingPrettyPrinted error:nil];
self.textView.text = [[NSString alloc] initWithData:jsonData encoding:NSUTF8StringEncoding]; // [JMHttpRequestMethod cleanNetWorkRefreshCache];
NSLog(@"缓存大小为%@", [JMHttpRequestMethod fileSizeWithDBPath]);
} fail:^(NSError *error) { }];

第二种实现方式:

  功能:POST GET Upload download

#import "HYBNetworking.h"
#import "AFNetworkActivityIndicatorManager.h"
#import "AFNetworking.h"
#import "AFHTTPSessionManager.h" #import <CommonCrypto/CommonDigest.h> @interface NSString (md5) + (NSString *)hybnetworking_md5:(NSString *)string; @end @implementation NSString (md5) + (NSString *)hybnetworking_md5:(NSString *)string {
if (string == nil || [string length] == ) {
return nil;
} unsigned char digest[CC_MD5_DIGEST_LENGTH], i;
CC_MD5([string UTF8String], (int)[string lengthOfBytesUsingEncoding:NSUTF8StringEncoding], digest);
NSMutableString *ms = [NSMutableString string]; for (i = ; i < CC_MD5_DIGEST_LENGTH; i++) {
[ms appendFormat:@"%02x", (int)(digest[i])];
} return [ms copy];
} @end static NSString *sg_privateNetworkBaseUrl = nil;
static BOOL sg_isEnableInterfaceDebug = NO;
static BOOL sg_shouldAutoEncode = NO;
static NSDictionary *sg_httpHeaders = nil;
static HYBResponseType sg_responseType = kHYBResponseTypeJSON;
static HYBRequestType sg_requestType = kHYBRequestTypePlainText;
static HYBNetworkStatus sg_networkStatus = kHYBNetworkStatusReachableViaWiFi;
static NSMutableArray *sg_requestTasks;
static BOOL sg_cacheGet = YES;
static BOOL sg_cachePost = NO;
static BOOL sg_shouldCallbackOnCancelRequest = YES;
static NSTimeInterval sg_timeout = 60.0f;
static BOOL sg_shoulObtainLocalWhenUnconnected = NO;
static BOOL sg_isBaseURLChanged = YES;
static AFHTTPSessionManager *sg_sharedManager = nil;
static NSUInteger sg_maxCacheSize = ; @implementation HYBNetworking + (void)load {
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
// 尝试清除缓存
if (sg_maxCacheSize > && [self totalCacheSize] > * * sg_maxCacheSize) {
[self clearCaches];
}
});
} + (void)autoToClearCacheWithLimitedToSize:(NSUInteger)mSize {
sg_maxCacheSize = mSize;
} + (void)cacheGetRequest:(BOOL)isCacheGet shoulCachePost:(BOOL)shouldCachePost {
sg_cacheGet = isCacheGet;
sg_cachePost = shouldCachePost;
} + (void)updateBaseUrl:(NSString *)baseUrl {
if (![baseUrl isEqualToString:sg_privateNetworkBaseUrl] && baseUrl && baseUrl.length) {
sg_isBaseURLChanged = YES;
} else {
sg_isBaseURLChanged = NO;
} sg_privateNetworkBaseUrl = baseUrl;
} + (NSString *)baseUrl {
return sg_privateNetworkBaseUrl;
} + (void)setTimeout:(NSTimeInterval)timeout {
sg_timeout = timeout;
} + (void)obtainDataFromLocalWhenNetworkUnconnected:(BOOL)shouldObtain {
sg_shoulObtainLocalWhenUnconnected = shouldObtain;
if (sg_shoulObtainLocalWhenUnconnected && (sg_cacheGet || sg_cachePost)) {
[self detectNetwork];
}
} + (void)enableInterfaceDebug:(BOOL)isDebug {
sg_isEnableInterfaceDebug = isDebug;
} + (BOOL)isDebug {
return sg_isEnableInterfaceDebug;
} static inline NSString *cachePath() {
return [NSHomeDirectory() stringByAppendingPathComponent:@"Documents/HYBNetworkingCaches"];
} + (void)clearCaches {
NSString *directoryPath = cachePath(); if ([[NSFileManager defaultManager] fileExistsAtPath:directoryPath isDirectory:nil]) {
NSError *error = nil;
[[NSFileManager defaultManager] removeItemAtPath:directoryPath error:&error]; if (error) {
NSLog(@"HYBNetworking clear caches error: %@", error);
} else {
NSLog(@"HYBNetworking clear caches ok");
}
}
} + (unsigned long long)totalCacheSize {
NSString *directoryPath = cachePath();
BOOL isDir = NO;
unsigned long long total = ; if ([[NSFileManager defaultManager] fileExistsAtPath:directoryPath isDirectory:&isDir]) {
if (isDir) {
NSError *error = nil;
NSArray *array = [[NSFileManager defaultManager] contentsOfDirectoryAtPath:directoryPath error:&error]; if (error == nil) {
for (NSString *subpath in array) {
NSString *path = [directoryPath stringByAppendingPathComponent:subpath];
NSDictionary *dict = [[NSFileManager defaultManager] attributesOfItemAtPath:path
error:&error];
if (!error) {
total += [dict[NSFileSize] unsignedIntegerValue];
}
}
}
}
} return total;
} + (NSMutableArray *)allTasks {
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
if (sg_requestTasks == nil) {
sg_requestTasks = [[NSMutableArray alloc] init];
}
}); return sg_requestTasks;
} + (void)cancelAllRequest {
@synchronized(self) {
[[self allTasks] enumerateObjectsUsingBlock:^(HYBURLSessionTask * _Nonnull task, NSUInteger idx, BOOL * _Nonnull stop) {
if ([task isKindOfClass:[HYBURLSessionTask class]]) {
[task cancel];
}
}]; [[self allTasks] removeAllObjects];
};
} + (void)cancelRequestWithURL:(NSString *)url {
if (url == nil) {
return;
} @synchronized(self) {
[[self allTasks] enumerateObjectsUsingBlock:^(HYBURLSessionTask * _Nonnull task, NSUInteger idx, BOOL * _Nonnull stop) {
if ([task isKindOfClass:[HYBURLSessionTask class]]
&& [task.currentRequest.URL.absoluteString hasSuffix:url]) {
[task cancel];
[[self allTasks] removeObject:task];
return;
}
}];
};
} + (void)configRequestType:(HYBRequestType)requestType
responseType:(HYBResponseType)responseType
shouldAutoEncodeUrl:(BOOL)shouldAutoEncode
callbackOnCancelRequest:(BOOL)shouldCallbackOnCancelRequest {
sg_requestType = requestType;
sg_responseType = responseType;
sg_shouldAutoEncode = shouldAutoEncode;
sg_shouldCallbackOnCancelRequest = shouldCallbackOnCancelRequest;
} + (BOOL)shouldEncode {
return sg_shouldAutoEncode;
} + (void)configCommonHttpHeaders:(NSDictionary *)httpHeaders {
sg_httpHeaders = httpHeaders;
} + (HYBURLSessionTask *)getWithUrl:(NSString *)url
refreshCache:(BOOL)refreshCache
success:(HYBResponseSuccess)success
fail:(HYBResponseFail)fail {
return [self getWithUrl:url
refreshCache:refreshCache
params:nil
success:success
fail:fail];
} + (HYBURLSessionTask *)getWithUrl:(NSString *)url
refreshCache:(BOOL)refreshCache
params:(NSDictionary *)params
success:(HYBResponseSuccess)success
fail:(HYBResponseFail)fail {
return [self getWithUrl:url
refreshCache:refreshCache
params:params
progress:nil
success:success
fail:fail];
} + (HYBURLSessionTask *)getWithUrl:(NSString *)url
refreshCache:(BOOL)refreshCache
params:(NSDictionary *)params
progress:(HYBGetProgress)progress
success:(HYBResponseSuccess)success
fail:(HYBResponseFail)fail {
return [self _requestWithUrl:url
refreshCache:refreshCache
httpMedth:
params:params
progress:progress
success:success
fail:fail];
} + (HYBURLSessionTask *)postWithUrl:(NSString *)url
refreshCache:(BOOL)refreshCache
params:(NSDictionary *)params
success:(HYBResponseSuccess)success
fail:(HYBResponseFail)fail {
return [self postWithUrl:url
refreshCache:refreshCache
params:params
progress:nil
success:success
fail:fail];
} + (HYBURLSessionTask *)postWithUrl:(NSString *)url
refreshCache:(BOOL)refreshCache
params:(NSDictionary *)params
progress:(HYBPostProgress)progress
success:(HYBResponseSuccess)success
fail:(HYBResponseFail)fail {
return [self _requestWithUrl:url
refreshCache:refreshCache
httpMedth:
params:params
progress:progress
success:success
fail:fail];
} + (HYBURLSessionTask *)_requestWithUrl:(NSString *)url
refreshCache:(BOOL)refreshCache
httpMedth:(NSUInteger)httpMethod
params:(NSDictionary *)params
progress:(HYBDownloadProgress)progress
success:(HYBResponseSuccess)success
fail:(HYBResponseFail)fail {
if ([self shouldEncode]) {
url = [self encodeUrl:url];
} AFHTTPSessionManager *manager = [self manager];
NSString *absolute = [self absoluteUrlWithPath:url]; if ([self baseUrl] == nil) {
if ([NSURL URLWithString:url] == nil) {
HYBAppLog(@"URLString无效,无法生成URL。可能是URL中有中文,请尝试Encode URL");
return nil;
}
} else {
NSURL *absoluteURL = [NSURL URLWithString:absolute]; if (absoluteURL == nil) {
HYBAppLog(@"URLString无效,无法生成URL。可能是URL中有中文,请尝试Encode URL");
return nil;
}
} HYBURLSessionTask *session = nil; if (httpMethod == ) {
if (sg_cacheGet) {
if (sg_shoulObtainLocalWhenUnconnected) {
if (sg_networkStatus == kHYBNetworkStatusNotReachable || sg_networkStatus == kHYBNetworkStatusUnknown ) {
id response = [HYBNetworking cahceResponseWithURL:absolute
parameters:params];
if (response) {
if (success) {
[self successResponse:response callback:success]; if ([self isDebug]) {
[self logWithSuccessResponse:response
url:absolute
params:params];
}
}
return nil;
}
}
}
if (!refreshCache) {// 获取缓存
id response = [HYBNetworking cahceResponseWithURL:absolute
parameters:params];
if (response) {
if (success) {
[self successResponse:response callback:success]; if ([self isDebug]) {
[self logWithSuccessResponse:response
url:absolute
params:params];
}
}
return nil;
}
}
} session = [manager GET:url parameters:params progress:^(NSProgress * _Nonnull downloadProgress) {
if (progress) {
progress(downloadProgress.completedUnitCount, downloadProgress.totalUnitCount);
}
} success:^(NSURLSessionDataTask * _Nonnull task, id _Nullable responseObject) {
[self successResponse:responseObject callback:success]; if (sg_cacheGet) {
[self cacheResponseObject:responseObject request:task.currentRequest parameters:params];
} [[self allTasks] removeObject:task]; if ([self isDebug]) {
[self logWithSuccessResponse:responseObject
url:absolute
params:params];
}
} failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {
[[self allTasks] removeObject:task]; if ([error code] < && sg_cacheGet) {// 获取缓存
id response = [HYBNetworking cahceResponseWithURL:absolute
parameters:params];
if (response) {
if (success) {
[self successResponse:response callback:success]; if ([self isDebug]) {
[self logWithSuccessResponse:response
url:absolute
params:params];
}
}
} else {
[self handleCallbackWithError:error fail:fail]; if ([self isDebug]) {
[self logWithFailError:error url:absolute params:params];
}
}
} else {
[self handleCallbackWithError:error fail:fail]; if ([self isDebug]) {
[self logWithFailError:error url:absolute params:params];
}
}
}];
} else if (httpMethod == ) {
if (sg_cachePost ) {// 获取缓存
if (sg_shoulObtainLocalWhenUnconnected) {
if (sg_networkStatus == kHYBNetworkStatusNotReachable || sg_networkStatus == kHYBNetworkStatusUnknown ) {
id response = [HYBNetworking cahceResponseWithURL:absolute
parameters:params];
if (response) {
if (success) {
[self successResponse:response callback:success]; if ([self isDebug]) {
[self logWithSuccessResponse:response
url:absolute
params:params];
}
}
return nil;
}
}
}
if (!refreshCache) {
id response = [HYBNetworking cahceResponseWithURL:absolute
parameters:params];
if (response) {
if (success) {
[self successResponse:response callback:success]; if ([self isDebug]) {
[self logWithSuccessResponse:response
url:absolute
params:params];
}
}
return nil;
}
}
} session = [manager POST:url parameters:params progress:^(NSProgress * _Nonnull downloadProgress) {
if (progress) {
progress(downloadProgress.completedUnitCount, downloadProgress.totalUnitCount);
}
} success:^(NSURLSessionDataTask * _Nonnull task, id _Nullable responseObject) {
[self successResponse:responseObject callback:success]; if (sg_cachePost) {
[self cacheResponseObject:responseObject request:task.currentRequest parameters:params];
} [[self allTasks] removeObject:task]; if ([self isDebug]) {
[self logWithSuccessResponse:responseObject
url:absolute
params:params];
}
} failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {
[[self allTasks] removeObject:task]; if ([error code] < && sg_cachePost) {// 获取缓存
id response = [HYBNetworking cahceResponseWithURL:absolute
parameters:params]; if (response) {
if (success) {
[self successResponse:response callback:success]; if ([self isDebug]) {
[self logWithSuccessResponse:response
url:absolute
params:params];
}
}
} else {
[self handleCallbackWithError:error fail:fail]; if ([self isDebug]) {
[self logWithFailError:error url:absolute params:params];
}
}
} else {
[self handleCallbackWithError:error fail:fail]; if ([self isDebug]) {
[self logWithFailError:error url:absolute params:params];
}
}
}];
} if (session) {
[[self allTasks] addObject:session];
} return session;
} + (HYBURLSessionTask *)uploadFileWithUrl:(NSString *)url
uploadingFile:(NSString *)uploadingFile
progress:(HYBUploadProgress)progress
success:(HYBResponseSuccess)success
fail:(HYBResponseFail)fail {
if ([NSURL URLWithString:uploadingFile] == nil) {
HYBAppLog(@"uploadingFile无效,无法生成URL。请检查待上传文件是否存在");
return nil;
} NSURL *uploadURL = nil;
if ([self baseUrl] == nil) {
uploadURL = [NSURL URLWithString:url];
} else {
uploadURL = [NSURL URLWithString:[NSString stringWithFormat:@"%@%@", [self baseUrl], url]];
} if (uploadURL == nil) {
HYBAppLog(@"URLString无效,无法生成URL。可能是URL中有中文或特殊字符,请尝试Encode URL");
return nil;
} AFHTTPSessionManager *manager = [self manager];
NSURLRequest *request = [NSURLRequest requestWithURL:uploadURL];
HYBURLSessionTask *session = nil; [manager uploadTaskWithRequest:request fromFile:[NSURL URLWithString:uploadingFile] progress:^(NSProgress * _Nonnull uploadProgress) {
if (progress) {
progress(uploadProgress.completedUnitCount, uploadProgress.totalUnitCount);
}
} completionHandler:^(NSURLResponse * _Nonnull response, id _Nullable responseObject, NSError * _Nullable error) {
[[self allTasks] removeObject:session]; [self successResponse:responseObject callback:success]; if (error) {
[self handleCallbackWithError:error fail:fail]; if ([self isDebug]) {
[self logWithFailError:error url:response.URL.absoluteString params:nil];
}
} else {
if ([self isDebug]) {
[self logWithSuccessResponse:responseObject
url:response.URL.absoluteString
params:nil];
}
}
}]; if (session) {
[[self allTasks] addObject:session];
} return session;
} + (HYBURLSessionTask *)uploadWithImage:(UIImage *)image
url:(NSString *)url
filename:(NSString *)filename
name:(NSString *)name
mimeType:(NSString *)mimeType
parameters:(NSDictionary *)parameters
progress:(HYBUploadProgress)progress
success:(HYBResponseSuccess)success
fail:(HYBResponseFail)fail {
if ([self baseUrl] == nil) {
if ([NSURL URLWithString:url] == nil) {
HYBAppLog(@"URLString无效,无法生成URL。可能是URL中有中文,请尝试Encode URL");
return nil;
}
} else {
if ([NSURL URLWithString:[NSString stringWithFormat:@"%@%@", [self baseUrl], url]] == nil) {
HYBAppLog(@"URLString无效,无法生成URL。可能是URL中有中文,请尝试Encode URL");
return nil;
}
} if ([self shouldEncode]) {
url = [self encodeUrl:url];
} NSString *absolute = [self absoluteUrlWithPath:url]; AFHTTPSessionManager *manager = [self manager];
HYBURLSessionTask *session = [manager POST:url parameters:parameters constructingBodyWithBlock:^(id<AFMultipartFormData> _Nonnull formData) {
NSData *imageData = UIImageJPEGRepresentation(image, ); NSString *imageFileName = filename;
if (filename == nil || ![filename isKindOfClass:[NSString class]] || filename.length == ) {
NSDateFormatter *formatter = [[NSDateFormatter alloc] init];
formatter.dateFormat = @"yyyyMMddHHmmss";
NSString *str = [formatter stringFromDate:[NSDate date]];
imageFileName = [NSString stringWithFormat:@"%@.jpg", str];
} // 上传图片,以文件流的格式
[formData appendPartWithFileData:imageData name:name fileName:imageFileName mimeType:mimeType];
} progress:^(NSProgress * _Nonnull uploadProgress) {
if (progress) {
progress(uploadProgress.completedUnitCount, uploadProgress.totalUnitCount);
}
} success:^(NSURLSessionDataTask * _Nonnull task, id _Nullable responseObject) {
[[self allTasks] removeObject:task];
[self successResponse:responseObject callback:success]; if ([self isDebug]) {
[self logWithSuccessResponse:responseObject
url:absolute
params:parameters];
}
} failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {
[[self allTasks] removeObject:task]; [self handleCallbackWithError:error fail:fail]; if ([self isDebug]) {
[self logWithFailError:error url:absolute params:nil];
}
}]; [session resume];
if (session) {
[[self allTasks] addObject:session];
} return session;
} + (HYBURLSessionTask *)downloadWithUrl:(NSString *)url
saveToPath:(NSString *)saveToPath
progress:(HYBDownloadProgress)progressBlock
success:(HYBResponseSuccess)success
failure:(HYBResponseFail)failure {
if ([self baseUrl] == nil) {
if ([NSURL URLWithString:url] == nil) {
HYBAppLog(@"URLString无效,无法生成URL。可能是URL中有中文,请尝试Encode URL");
return nil;
}
} else {
if ([NSURL URLWithString:[NSString stringWithFormat:@"%@%@", [self baseUrl], url]] == nil) {
HYBAppLog(@"URLString无效,无法生成URL。可能是URL中有中文,请尝试Encode URL");
return nil;
}
} NSURLRequest *downloadRequest = [NSURLRequest requestWithURL:[NSURL URLWithString:url]];
AFHTTPSessionManager *manager = [self manager]; HYBURLSessionTask *session = nil; session = [manager downloadTaskWithRequest:downloadRequest progress:^(NSProgress * _Nonnull downloadProgress) {
if (progressBlock) {
progressBlock(downloadProgress.completedUnitCount, downloadProgress.totalUnitCount);
}
} destination:^NSURL * _Nonnull(NSURL * _Nonnull targetPath, NSURLResponse * _Nonnull response) {
return [NSURL fileURLWithPath:saveToPath];
} completionHandler:^(NSURLResponse * _Nonnull response, NSURL * _Nullable filePath, NSError * _Nullable error) {
[[self allTasks] removeObject:session]; if (error == nil) {
if (success) {
success(filePath.absoluteString);
} if ([self isDebug]) {
HYBAppLog(@"Download success for url %@",
[self absoluteUrlWithPath:url]);
}
} else {
[self handleCallbackWithError:error fail:failure]; if ([self isDebug]) {
HYBAppLog(@"Download fail for url %@, reason : %@",
[self absoluteUrlWithPath:url],
[error description]);
}
}
}]; [session resume];
if (session) {
[[self allTasks] addObject:session];
} return session;
} #pragma mark - Private
+ (AFHTTPSessionManager *)manager {
@synchronized (self) {
// 只要不切换baseurl,就一直使用同一个session manager
if (sg_sharedManager == nil || sg_isBaseURLChanged) {
// 开启转圈圈
[AFNetworkActivityIndicatorManager sharedManager].enabled = YES; AFHTTPSessionManager *manager = nil;;
if ([self baseUrl] != nil) {
    #warning session 容易造成循环引用 delegate 是强引用类型 解决方法:1.通过单例的方式创建 2.delloc中释放delegate
manager = [[AFHTTPSessionManager alloc] initWithBaseURL:[NSURL URLWithString:[self baseUrl]]];
} else {
manager = [AFHTTPSessionManager manager];
} switch (sg_requestType) {
case kHYBRequestTypeJSON: {
manager.requestSerializer = [AFJSONRequestSerializer serializer];
break;
}
case kHYBRequestTypePlainText: {
manager.requestSerializer = [AFHTTPRequestSerializer serializer];
break;
}
default: {
break;
}
} switch (sg_responseType) {
case kHYBResponseTypeJSON: {
manager.responseSerializer = [AFJSONResponseSerializer serializer];
break;
}
case kHYBResponseTypeXML: {
manager.responseSerializer = [AFXMLParserResponseSerializer serializer];
break;
}
case kHYBResponseTypeData: {
manager.responseSerializer = [AFHTTPResponseSerializer serializer];
break;
}
default: {
break;
}
} manager.requestSerializer.stringEncoding = NSUTF8StringEncoding; for (NSString *key in sg_httpHeaders.allKeys) {
if (sg_httpHeaders[key] != nil) {
[manager.requestSerializer setValue:sg_httpHeaders[key] forHTTPHeaderField:key];
}
} manager.responseSerializer.acceptableContentTypes = [NSSet setWithArray:@[@"application/json",
@"text/html",
@"text/json",
@"text/plain",
@"text/javascript",
@"text/xml",
@"image/*"]]; manager.requestSerializer.timeoutInterval = sg_timeout; // 设置允许同时最大并发数量,过大容易出问题
manager.operationQueue.maxConcurrentOperationCount = ;
sg_sharedManager = manager;
}
} return sg_sharedManager;
} + (void)detectNetwork {
AFNetworkReachabilityManager *reachabilityManager = [AFNetworkReachabilityManager sharedManager]; [reachabilityManager startMonitoring];
[reachabilityManager setReachabilityStatusChangeBlock:^(AFNetworkReachabilityStatus status) {
if (status == AFNetworkReachabilityStatusNotReachable){
sg_networkStatus = kHYBNetworkStatusNotReachable;
} else if (status == AFNetworkReachabilityStatusUnknown){
sg_networkStatus = kHYBNetworkStatusUnknown;
} else if (status == AFNetworkReachabilityStatusReachableViaWWAN){
sg_networkStatus = kHYBNetworkStatusReachableViaWWAN;
} else if (status == AFNetworkReachabilityStatusReachableViaWiFi){
sg_networkStatus = kHYBNetworkStatusReachableViaWiFi;
}
}];
} + (void)logWithSuccessResponse:(id)response url:(NSString *)url params:(NSDictionary *)params {
HYBAppLog(@"\n");
HYBAppLog(@"\nRequest success, URL: %@\n params:%@\n response:%@\n\n",
[self generateGETAbsoluteURL:url params:params],
params,
[self tryToParseData:response]);
} + (void)logWithFailError:(NSError *)error url:(NSString *)url params:(id)params {
NSString *format = @" params: ";
if (params == nil || ![params isKindOfClass:[NSDictionary class]]) {
format = @"";
params = @"";
} HYBAppLog(@"\n");
if ([error code] == NSURLErrorCancelled) {
HYBAppLog(@"\nRequest was canceled mannully, URL: %@ %@%@\n\n",
[self generateGETAbsoluteURL:url params:params],
format,
params);
} else {
HYBAppLog(@"\nRequest error, URL: %@ %@%@\n errorInfos:%@\n\n",
[self generateGETAbsoluteURL:url params:params],
format,
params,
[error localizedDescription]);
}
} // 仅对一级字典结构起作用
+ (NSString *)generateGETAbsoluteURL:(NSString *)url params:(id)params {
if (params == nil || ![params isKindOfClass:[NSDictionary class]] || [params count] == ) {
return url;
} NSString *queries = @"";
for (NSString *key in params) {
id value = [params objectForKey:key]; if ([value isKindOfClass:[NSDictionary class]]) {
continue;
} else if ([value isKindOfClass:[NSArray class]]) {
continue;
} else if ([value isKindOfClass:[NSSet class]]) {
continue;
} else {
queries = [NSString stringWithFormat:@"%@%@=%@&",
(queries.length == ? @"&" : queries),
key,
value];
}
} if (queries.length > ) {
queries = [queries substringToIndex:queries.length - ];
} if (([url hasPrefix:@"http://"] || [url hasPrefix:@"https://"]) && queries.length > ) {
if ([url rangeOfString:@"?"].location != NSNotFound
|| [url rangeOfString:@"#"].location != NSNotFound) {
url = [NSString stringWithFormat:@"%@%@", url, queries];
} else {
queries = [queries substringFromIndex:];
url = [NSString stringWithFormat:@"%@?%@", url, queries];
}
} return url.length == ? queries : url;
} + (NSString *)encodeUrl:(NSString *)url {
return [self hyb_URLEncode:url];
} + (id)tryToParseData:(id)responseData {
if ([responseData isKindOfClass:[NSData class]]) {
// 尝试解析成JSON
if (responseData == nil) {
return responseData;
} else {
NSError *error = nil;
NSDictionary *response = [NSJSONSerialization JSONObjectWithData:responseData
options:NSJSONReadingMutableContainers
error:&error]; if (error != nil) {
return responseData;
} else {
return response;
}
}
} else {
return responseData;
}
} + (void)successResponse:(id)responseData callback:(HYBResponseSuccess)success {
if (success) {
success([self tryToParseData:responseData]);
}
} + (NSString *)hyb_URLEncode:(NSString *)url {
return [url stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding]; // 采用下面的方式反而不能请求成功
// NSString *newString =
// CFBridgingRelease(CFURLCreateStringByAddingPercentEscapes(kCFAllocatorDefault,
// (CFStringRef)url,
// NULL,
// CFSTR(":/?#[]@!$ &'()*+,;=\"<>%{}|\\^~`"), CFStringConvertNSStringEncodingToEncoding(NSUTF8StringEncoding)));
// if (newString) {
// return newString;
// }
//
// return url;
} + (id)cahceResponseWithURL:(NSString *)url parameters:params {
id cacheData = nil; if (url) {
// Try to get datas from disk
NSString *directoryPath = cachePath();
NSString *absoluteURL = [self generateGETAbsoluteURL:url params:params];
NSString *key = [NSString hybnetworking_md5:absoluteURL];
NSString *path = [directoryPath stringByAppendingPathComponent:key]; NSData *data = [[NSFileManager defaultManager] contentsAtPath:path];
if (data) {
cacheData = data;
HYBAppLog(@"Read data from cache for url: %@\n", url);
}
} return cacheData;
} + (void)cacheResponseObject:(id)responseObject request:(NSURLRequest *)request parameters:params {
if (request && responseObject && ![responseObject isKindOfClass:[NSNull class]]) {
NSString *directoryPath = cachePath(); NSError *error = nil; if (![[NSFileManager defaultManager] fileExistsAtPath:directoryPath isDirectory:nil]) {
[[NSFileManager defaultManager] createDirectoryAtPath:directoryPath
withIntermediateDirectories:YES
attributes:nil
error:&error];
if (error) {
HYBAppLog(@"create cache dir error: %@\n", error);
return;
}
} NSString *absoluteURL = [self generateGETAbsoluteURL:request.URL.absoluteString params:params];
NSString *key = [NSString hybnetworking_md5:absoluteURL];
NSString *path = [directoryPath stringByAppendingPathComponent:key];
NSDictionary *dict = (NSDictionary *)responseObject; NSData *data = nil;
if ([dict isKindOfClass:[NSData class]]) {
data = responseObject;
} else {
data = [NSJSONSerialization dataWithJSONObject:dict
options:NSJSONWritingPrettyPrinted
error:&error];
} if (data && error == nil) {
BOOL isOk = [[NSFileManager defaultManager] createFileAtPath:path contents:data attributes:nil];
if (isOk) {
HYBAppLog(@"cache file ok for request: %@\n", absoluteURL);
} else {
HYBAppLog(@"cache file error for request: %@\n", absoluteURL);
}
}
}
} + (NSString *)absoluteUrlWithPath:(NSString *)path {
if (path == nil || path.length == ) {
return @"";
} if ([self baseUrl] == nil || [[self baseUrl] length] == ) {
return path;
} NSString *absoluteUrl = path; if (![path hasPrefix:@"http://"] && ![path hasPrefix:@"https://"]) {
if ([[self baseUrl] hasSuffix:@"/"]) {
if ([path hasPrefix:@"/"]) {
NSMutableString * mutablePath = [NSMutableString stringWithString:path];
[mutablePath deleteCharactersInRange:NSMakeRange(, )];
absoluteUrl = [NSString stringWithFormat:@"%@%@",
[self baseUrl], mutablePath];
} else {
absoluteUrl = [NSString stringWithFormat:@"%@%@",[self baseUrl], path];
}
} else {
if ([path hasPrefix:@"/"]) {
absoluteUrl = [NSString stringWithFormat:@"%@%@",[self baseUrl], path];
} else {
absoluteUrl = [NSString stringWithFormat:@"%@/%@",
[self baseUrl], path];
}
}
} return absoluteUrl;
} + (void)handleCallbackWithError:(NSError *)error fail:(HYBResponseFail)fail {
if ([error code] == NSURLErrorCancelled) {
if (sg_shouldCallbackOnCancelRequest) {
if (fail) {
fail(error);
}
}
} else {
if (fail) {
fail(error);
}
}
} @end
#import <Foundation/Foundation.h>
#import <UIKit/UIKit.h> // 项目打包上线都不会打印日志,因此可放心。
#ifdef DEBUG
#define HYBAppLog(s, ... ) NSLog( @"[%@ in line %d] ===============>%@", [[NSString stringWithUTF8String:__FILE__] lastPathComponent], __LINE__, [NSString stringWithFormat:(s), ##__VA_ARGS__] )
#else
#define HYBAppLog(s, ... )
#endif /*!
* @author 黄, 16-01-08 14:01:26
*
* 下载进度
*
* @param bytesRead 已下载的大小
* @param totalBytesRead 文件总大小
* @param totalBytesExpectedToRead 还有多少需要下载
*/
typedef void (^HYBDownloadProgress)(int64_t bytesRead,
int64_t totalBytesRead); typedef HYBDownloadProgress HYBGetProgress;
typedef HYBDownloadProgress HYBPostProgress; /*!
* @author 黄, 16-01-08 14:01:26
*
* 上传进度
*
* @param bytesWritten 已上传的大小
* @param totalBytesWritten 总上传大小
*/
typedef void (^HYBUploadProgress)(int64_t bytesWritten,
int64_t totalBytesWritten); typedef NS_ENUM(NSUInteger, HYBResponseType) {
kHYBResponseTypeJSON = , // 默认
kHYBResponseTypeXML = , // XML
// 特殊情况下,一转换服务器就无法识别的,默认会尝试转换成JSON,若失败则需要自己去转换
kHYBResponseTypeData =
}; typedef NS_ENUM(NSUInteger, HYBRequestType) {
kHYBRequestTypeJSON = , // 默认
kHYBRequestTypePlainText = // 普通text/html
}; typedef NS_ENUM(NSInteger, HYBNetworkStatus) {
kHYBNetworkStatusUnknown = -,//未知网络
kHYBNetworkStatusNotReachable = ,//网络无连接
kHYBNetworkStatusReachableViaWWAN = ,//2,3,4G网络
kHYBNetworkStatusReachableViaWiFi = ,//WIFI网络
}; @class NSURLSessionTask; // 请勿直接使用NSURLSessionDataTask,以减少对第三方的依赖
// 所有接口返回的类型都是基类NSURLSessionTask,若要接收返回值
// 且处理,请转换成对应的子类类型
typedef NSURLSessionTask HYBURLSessionTask;
typedef void(^HYBResponseSuccess)(id response);
typedef void(^HYBResponseFail)(NSError *error); /*!
* @author huangyibiao, 15-11-15 13:11:31
*
* 基于AFNetworking的网络层封装类.
*
* @note 这里只提供公共api
*/
@interface HYBNetworking : NSObject /*!
* @author 黄, 15-11-15 13:11:45
*
* 用于指定网络请求接口的基础url,如:
* http://henishuo.com或者http://101.200.209.244
* 通常在AppDelegate中启动时就设置一次就可以了。如果接口有来源
* 于多个服务器,可以调用更新
*
* @param baseUrl 网络接口的基础url
*/
+ (void)updateBaseUrl:(NSString *)baseUrl;
+ (NSString *)baseUrl; /**
* 设置请求超时时间,默认为60秒
*
* @param timeout 超时时间
*/
+ (void)setTimeout:(NSTimeInterval)timeout; /**
* 当检查到网络异常时,是否从从本地提取数据。默认为NO。一旦设置为YES,当设置刷新缓存时,
* 若网络异常也会从缓存中读取数据。同样,如果设置超时不回调,同样也会在网络异常时回调,除非
* 本地没有数据!
*
* @param shouldObtain YES/NO
*/
+ (void)obtainDataFromLocalWhenNetworkUnconnected:(BOOL)shouldObtain; /**
* @author 黄
*
* 默认只缓存GET请求的数据,对于POST请求是不缓存的。如果要缓存POST获取的数据,需要手动调用设置
* 对JSON类型数据有效,对于PLIST、XML不确定!
*
* @param isCacheGet 默认为YES
* @param shouldCachePost 默认为NO
*/
+ (void)cacheGetRequest:(BOOL)isCacheGet shoulCachePost:(BOOL)shouldCachePost; /**
* @author 黄
*
* 获取缓存总大小/bytes
*
* @return 缓存大小
*/
+ (unsigned long long)totalCacheSize; /**
* 默认不会自动清除缓存,如果需要,可以设置自动清除缓存,并且需要指定上限。当指定上限>0M时,
* 若缓存达到了上限值,则每次启动应用则尝试自动去清理缓存。
*
* @param mSize 缓存上限大小,单位为M(兆),默认为0,表示不清理
*/
+ (void)autoToClearCacheWithLimitedToSize:(NSUInteger)mSize; /**
* @author 黄
*
* 清除缓存
*/
+ (void)clearCaches; /*!
* @author 黄, 15-11-15 14:11:40
*
* 开启或关闭接口打印信息
*
* @param isDebug 开发期,最好打开,默认是NO
*/
+ (void)enableInterfaceDebug:(BOOL)isDebug; /*!
* @author 黄, 15-12-25 15:12:45
*
* 配置请求格式,默认为JSON。如果要求传XML或者PLIST,请在全局配置一下
*
* @param requestType 请求格式,默认为JSON
* @param responseType 响应格式,默认为JSO,
* @param shouldAutoEncode YES or NO,默认为NO,是否自动encode url
* @param shouldCallbackOnCancelRequest 当取消请求时,是否要回调,默认为YES
*/
+ (void)configRequestType:(HYBRequestType)requestType
responseType:(HYBResponseType)responseType
shouldAutoEncodeUrl:(BOOL)shouldAutoEncode
callbackOnCancelRequest:(BOOL)shouldCallbackOnCancelRequest; /*!
* @author 黄, 15-11-16 13:11:41
*
* 配置公共的请求头,只调用一次即可,通常放在应用启动的时候配置就可以了
*
* @param httpHeaders 只需要将与服务器商定的固定参数设置即可
*/
+ (void)configCommonHttpHeaders:(NSDictionary *)httpHeaders; /**
* @author 黄
*
* 取消所有请求
*/
+ (void)cancelAllRequest;
/**
* @author 黄
*
* 取消某个请求。如果是要取消某个请求,最好是引用接口所返回来的HYBURLSessionTask对象,
* 然后调用对象的cancel方法。如果不想引用对象,这里额外提供了一种方法来实现取消某个请求
*
* @param url URL,可以是绝对URL,也可以是path(也就是不包括baseurl)
*/
+ (void)cancelRequestWithURL:(NSString *)url; /*!
* @author 黄, 15-11-15 13:11:50
*
* GET请求接口,若不指定baseurl,可传完整的url
*
* @param url 接口路径,如/path/getArticleList
* @param refreshCache 是否刷新缓存。由于请求成功也可能没有数据,对于业务失败,只能通过人为手动判断
* @param params 接口中所需要的拼接参数,如@{"categoryid" : @(12)}
* @param success 接口成功请求到数据的回调
* @param fail 接口请求数据失败的回调
*
* @return 返回的对象中有可取消请求的API
*/
+ (HYBURLSessionTask *)getWithUrl:(NSString *)url
refreshCache:(BOOL)refreshCache
success:(HYBResponseSuccess)success
fail:(HYBResponseFail)fail;
// 多一个params参数
+ (HYBURLSessionTask *)getWithUrl:(NSString *)url
refreshCache:(BOOL)refreshCache
params:(NSDictionary *)params
success:(HYBResponseSuccess)success
fail:(HYBResponseFail)fail;
// 多一个带进度回调
+ (HYBURLSessionTask *)getWithUrl:(NSString *)url
refreshCache:(BOOL)refreshCache
params:(NSDictionary *)params
progress:(HYBGetProgress)progress
success:(HYBResponseSuccess)success
fail:(HYBResponseFail)fail; /*!
* @author 黄, 15-11-15 13:11:50
*
* POST请求接口,若不指定baseurl,可传完整的url
*
* @param url 接口路径,如/path/getArticleList
* @param params 接口中所需的参数,如@{"categoryid" : @(12)}
* @param success 接口成功请求到数据的回调
* @param fail 接口请求数据失败的回调
*
* @return 返回的对象中有可取消请求的API
*/
+ (HYBURLSessionTask *)postWithUrl:(NSString *)url
refreshCache:(BOOL)refreshCache
params:(NSDictionary *)params
success:(HYBResponseSuccess)success
fail:(HYBResponseFail)fail;
+ (HYBURLSessionTask *)postWithUrl:(NSString *)url
refreshCache:(BOOL)refreshCache
params:(NSDictionary *)params
progress:(HYBPostProgress)progress
success:(HYBResponseSuccess)success
fail:(HYBResponseFail)fail;
/**
* @author 黄, 16-01-31 00:01:40
*
* 图片上传接口,若不指定baseurl,可传完整的url
*
* @param image 图片对象
* @param url 上传图片的接口路径,如/path/images/
* @param filename 给图片起一个名字,默认为当前日期时间,格式为"yyyyMMddHHmmss",后缀为`jpg`
* @param name 与指定的图片相关联的名称,这是由后端写接口的人指定的,如imagefiles
* @param mimeType 默认为image/jpeg
* @param parameters 参数
* @param progress 上传进度
* @param success 上传成功回调
* @param fail 上传失败回调
*
* @return
*/
+ (HYBURLSessionTask *)uploadWithImage:(UIImage *)image
url:(NSString *)url
filename:(NSString *)filename
name:(NSString *)name
mimeType:(NSString *)mimeType
parameters:(NSDictionary *)parameters
progress:(HYBUploadProgress)progress
success:(HYBResponseSuccess)success
fail:(HYBResponseFail)fail; /**
* @author 黄, 16-01-31 00:01:59
*
* 上传文件操作
*
* @param url 上传路径
* @param uploadingFile 待上传文件的路径
* @param progress 上传进度
* @param success 上传成功回调
* @param fail 上传失败回调
*
* @return
*/
+ (HYBURLSessionTask *)uploadFileWithUrl:(NSString *)url
uploadingFile:(NSString *)uploadingFile
progress:(HYBUploadProgress)progress
success:(HYBResponseSuccess)success
fail:(HYBResponseFail)fail; /*!
* @author 黄, 16-01-08 15:01:11
*
* 下载文件
*
* @param url 下载URL
* @param saveToPath 下载到哪个路径下
* @param progressBlock 下载进度
* @param success 下载成功后的回调
* @param failure 下载失败后的回调
*/
+ (HYBURLSessionTask *)downloadWithUrl:(NSString *)url
saveToPath:(NSString *)saveToPath
progress:(HYBDownloadProgress)progressBlock
success:(HYBResponseSuccess)success
failure:(HYBResponseFail)failure; @end

使用:

  // 通常放在appdelegate就可以了
[HYBNetworking updateBaseUrl:@"http://apistore.baidu.com"];
[HYBNetworking enableInterfaceDebug:YES]; // 配置请求和响应类型,由于部分伙伴们的服务器不接收JSON传过去,现在默认值改成了plainText
[HYBNetworking configRequestType:kHYBRequestTypePlainText
responseType:kHYBResponseTypeJSON
shouldAutoEncodeUrl:YES
callbackOnCancelRequest:NO]; /*
[HYBNetworking.m:in line: 189]-->[message:
absoluteUrl: http://apistore.baidu.com/microservice/cityinfo?cityname=%E5%8C%97%E4%BA%AC
params:(null)
response:{
errNum = 0;
retData = {
cityCode = 101010100;
cityName = "\U5317\U4eac";
provinceName = "\U5317\U4eac";
telAreaCode = 010;
zipCode = 100000;
};
retMsg = success;
}
]
*/ // 设置GET、POST请求都缓存
[HYBNetworking cacheGetRequest:YES shoulCachePost:YES]; // 测试GET API
NSString *url = @"http://api.map.baidu.com/telematics/v3/weather?location=嘉兴&output=json&ak=5slgyqGDENN7Sy7pw29IUvrZ";
// 设置请求类型为text/html类型
// [HYBNetworking configRequestType:kHYBRequestTypePlainText];
// [HYBNetworking configResponseType:kHYBResponseTypeData];
// 如果请求回来的数据是业务数据,但是是失败的,这时候需要外部开发人员才能判断是业务失败。
// 内部处理是只有走failure的才能判断为无效数据,才不会缓存
// 如果设置为YES,则每次会去刷新缓存,也就是不会读取缓存,即使已经缓存起来
// 新下载的数据会重新缓存起来
[HYBNetworking getWithUrl:url refreshCache:NO params:nil progress:^(int64_t bytesRead, int64_t totalBytesRead) {
NSLog(@"progress: %f, cur: %lld, total: %lld",
(bytesRead * 1.0) / totalBytesRead,
bytesRead,
totalBytesRead);
} success:^(id response) { } fail:^(NSError *error) { }]; // 测试POST API:
// 假数据
NSDictionary *postDict = @{ @"urls": @"http://www.henishuo.com/git-use-inwork/",
@"goal" : @"site",
@"total" : @()
};
NSString *path = @"/urls?site=www.henishuo.com&token=bRidefmXoNxIi3Jp";
// 由于这里有两套基础路径,用时就需要更新
[HYBNetworking updateBaseUrl:@"http://data.zz.baidu.com"];
// 每次刷新缓存
// 如果获取到的业务数据是不正确的,则需要下次调用时设置为YES,表示要刷新缓存
// HYBURLSessionTask *task =
[HYBNetworking postWithUrl:path refreshCache:YES params:postDict success:^(id response) { } fail:^(NSError *error) { }]; // 取消全部请求
// [HYBNetworking cancelAllRequest]; // 取消单个请求方法一
// [HYBNetworking cancelRequestWithURL:path]; // 取消单个请求方法二
// [task cancel]; NSLog(@"%lld", [HYBNetworking totalCacheSize]);
// [HYBNetworking clearCaches]; path = [NSHomeDirectory() stringByAppendingPathComponent:@"Documents/b.zip"];
[HYBNetworking downloadWithUrl:@"http://wiki.lbsyun.baidu.com/cms/iossdk/sdk/BaiduMap_IOSSDK_v2.10.2_All.zip" saveToPath:path progress:^(int64_t bytesRead, int64_t totalBytesRead) { } success:^(id response) { } failure:^(NSError *error) { }];
// NSLog(@"%@", task);
}
04-25 05:59