diff --git a/Classes/Database/HDBMgr.h b/Classes/Database/HDBMgr.h index 7e5ca98..9249d14 100644 --- a/Classes/Database/HDBMgr.h +++ b/Classes/Database/HDBMgr.h @@ -53,6 +53,15 @@ //name: name of db, config in HDBMgrDatasource + (FMDatabaseQueue *)queueWithKey:(NSString *)key; +//close queue ++ (void)closeQueue:(NSString *)key; ++ (void)closeAllQueue; + + + +//reload queue ++ (void)reloadQueue:(NSString *)key; ++ (void)reloadAllQueue; @end diff --git a/Classes/Database/HDBMgr.m b/Classes/Database/HDBMgr.m index 9003939..0d2a745 100644 --- a/Classes/Database/HDBMgr.m +++ b/Classes/Database/HDBMgr.m @@ -66,18 +66,36 @@ - (FMDatabaseQueue *)queueWithKey:(NSString *)key }); return res; } -//- (FMDatabaseQueue *)defaultQueue -//{ -// dispatch_sync(self.operateQueue, ^{ -// if (!_defaultQueue) -// { -// _defaultQueue = [self queueInitWithKey:[self defaultDatabaseKey]]; -// } -// }); -// return _defaultQueue; -//} - +//close queue ++ (void)closeQueue:(NSString *)key +{ + dispatch_sync([HDBMgr shared].operateQueue, ^{ + FMDatabaseQueue *queue = [HDBMgr shared].queuesDict[key]; + [queue close]; + [[HDBMgr shared].queuesDict removeObjectForKey:key]; + }); +} ++ (void)closeAllQueue +{ + dispatch_sync([HDBMgr shared].operateQueue, ^{ + for (NSString *key in [HDBMgr shared].queuesDict) + { + FMDatabaseQueue *queue = [HDBMgr shared].queuesDict[key]; + [queue close]; + } + [[HDBMgr shared].queuesDict removeAllObjects]; + }); +} +//reload queue ++ (void)reloadQueue:(NSString *)key +{ + [self closeQueue:key]; +} ++ (void)reloadAllQueue +{ + [self closeAllQueue]; +} #pragma mark - db connect - (id)getDBSourceWithKey:(NSString *)key { diff --git a/Classes/Database/HDatabaseDAO.m b/Classes/Database/HDatabaseDAO.m index 6e77269..96e504f 100755 --- a/Classes/Database/HDatabaseDAO.m +++ b/Classes/Database/HDatabaseDAO.m @@ -9,7 +9,7 @@ #import "HDatabaseDAO.h" #import "HPropertyMgr.h" #import -#import +#import /** @@ -56,7 +56,7 @@ - (void)setWithResultSet:(FMResultSet*)result self.ID = [result stringForColumn:@"id"]; self.created = [result longForColumn:@"created"]; self.modified = [result longForColumn:@"modified"]; - NSArray *pplist = [[HPropertyMgr shared] entityPropertylist:NSStringFromClass(self.class) isDepSearch:YES]; + NSArray *pplist = [[HPropertyMgr shared] entityPropertylist:NSStringFromClass(self.class) deepTo:[HEntity class]]; for (NSString *p in pplist) { NSArray *exts = [[self class] annotations:p]; @@ -207,8 +207,7 @@ - (BOOL)save:(HEntity *)entity - (BOOL)save:(HEntity *)entity keypp:(NSString *)keypp { if (keypp.length == 0) return NO; - NSString *IDValue = [HDatabaseDAO cleanValue:entity.ID]; - if (![keypp isEqualToString:@"id"]) IDValue = [entity hValueForKey:keypp]; + NSString *IDValue = [HDatabaseDAO cleanValue:[entity hValueForKey:keypp]]; if(nil == [self get:IDValue]) { return [self add:entity]; @@ -227,8 +226,8 @@ - (BOOL)add:(HEntity *)entity [self inDatabase:^(FMDatabase* db) { - NSArray *pplist = [self entityPropertylist:[entity class] isDepSearch:NO]; - NSString *fields = [self entityPropertylistString:[entity class] isDepSearch:NO]; + NSArray *pplist = [self entityPropertylist:[entity class]]; + NSString *fields = [self entityPropertylistString:[entity class]]; NSMutableString *values = [[NSMutableString alloc] init]; int index = 0; for (NSString *p in pplist) @@ -299,7 +298,7 @@ - (BOOL)update:(HEntity *)entity keypp:(NSString *)keypp [self inDatabase:^(FMDatabase* db) { NSMutableString *settes =[[NSMutableString alloc] init]; - NSArray *pplist = [self entityPropertylist:[entity class] isDepSearch:NO]; + NSArray *pplist = [self entityPropertylist:[entity class]]; int index = 0; for (NSString *p in pplist) { @@ -313,8 +312,7 @@ - (BOOL)update:(HEntity *)entity keypp:(NSString *)keypp long nowtime = time(NULL); [settes appendFormat:@", modified = '%li'", nowtime]; - NSString *IDValue = [HDatabaseDAO cleanValue:entity.ID]; - if (![keypp isEqualToString:@"id"]) IDValue = [entity hValueForKey:keypp]; + NSString *IDValue = [HDatabaseDAO cleanValue:[entity hValueForKey:keypp]]; NSString *sql = [NSString stringWithFormat:@"UPDATE %@ SET %@ WHERE %@ = '%@'", [self tableName], settes, keypp, IDValue]; res = [db executeUpdate:sql]; if (res) @@ -331,7 +329,7 @@ - (BOOL)update:(HEntity *)entity keyppList:(NSArray *)keyppList [self inDatabase:^(FMDatabase* db) { NSMutableString *settes =[[NSMutableString alloc] init]; - NSArray *pplist = [self entityPropertylist:[entity class] isDepSearch:NO]; + NSArray *pplist = [self entityPropertylist:[entity class]]; int index = 0; for (NSString *p in pplist) { @@ -350,8 +348,7 @@ - (BOOL)update:(HEntity *)entity keyppList:(NSArray *)keyppList NSMutableString *whereStatment = [[NSMutableString alloc] init]; for (NSString *pp in keyppList) { - id value = [HDatabaseDAO cleanValue:entity.ID]; - if (![pp isEqualToString:@"id"]) value = [entity hValueForKey:pp]; + id value = [HDatabaseDAO cleanValue:[entity hValueForKey:pp]]; [whereStatment appendFormat:@" and %@ = '%@'",pp,[HDatabaseDAO cleanValue:[value stringValue]]]; } NSString *sql = [NSString stringWithFormat:@"UPDATE %@ SET %@ WHERE 1 %@", [self tableName], settes, whereStatment]; @@ -459,8 +456,8 @@ - (BOOL)adds:(NSArray *)entities if (![self tableName]) return NO; if ([entities count] == 0) return NO; Class entityClass = [entities.lastObject class]; - NSArray *pplist = [self entityPropertylist:entityClass isDepSearch:NO]; - NSString *fields = [self entityPropertylistString:entityClass isDepSearch:NO]; + NSArray *pplist = [self entityPropertylist:entityClass]; + NSString *fields = [self entityPropertylistString:entityClass]; fields = [fields stringByAppendingString:@",created,modified"]; __block BOOL res = YES; @@ -725,9 +722,9 @@ - (NSString *)conditonsStringFromDict:(NSDictionary *)dict -- (NSArray *)entityPropertylist:(Class)entityClass isDepSearch:(BOOL)deepSearch +- (NSArray *)entityPropertylist:(Class)entityClass { - NSArray *pplist = [[HPropertyMgr shared] entityPropertylist:NSStringFromClass(entityClass) isDepSearch:deepSearch]; + NSArray *pplist = [[HPropertyMgr shared] entityPropertylist:NSStringFromClass(entityClass) deepTo:nil]; NSMutableArray *newPPlist = [NSMutableArray new]; for (NSString *p in pplist) { @@ -738,9 +735,9 @@ - (NSArray *)entityPropertylist:(Class)entityClass isDepSearch:(BOOL)deepSearch } return newPPlist; } -- (NSArray *)entityPropertyDetailList:(Class)entityClass isDepSearch:(BOOL)deepSearch +- (NSArray *)entityPropertyDetailList:(Class)entityClass { - NSArray *pplist = [[HPropertyMgr shared] entityPropertyDetailList:NSStringFromClass(entityClass) isDepSearch:deepSearch]; + NSArray *pplist = [[HPropertyMgr shared] entityPropertyDetailList:NSStringFromClass(entityClass) deepTo:nil]; NSMutableArray *newPPDetaillist = [NSMutableArray new]; for (HPropertyDetail *p in pplist) { @@ -754,11 +751,7 @@ - (NSArray *)entityPropertylist:(Class)entityClass isDepSearch:(BOOL)deepSearch - (NSString *)entityPropertylistString:(Class)entityClass { - return [self entityPropertylistString:entityClass isDepSearch:YES]; -} -- (NSString *)entityPropertylistString:(Class)entityClass isDepSearch:(BOOL)deepSearch; -{ - NSArray *pplist = [self entityPropertylist:entityClass isDepSearch:deepSearch]; + NSArray *pplist = [self entityPropertylist:entityClass]; NSMutableString *fields = [[NSMutableString alloc] init]; int index = 0; for (NSString *p in pplist) diff --git a/Classes/Database/HEntity+Persistence.m b/Classes/Database/HEntity+Persistence.m index 9b70982..d42b7a2 100644 --- a/Classes/Database/HEntity+Persistence.m +++ b/Classes/Database/HEntity+Persistence.m @@ -57,7 +57,7 @@ + (BOOL)adds:(NSArray *)entities - (BOOL)remove { NSString *keyPP = [self keyProperty]; - id value = [self hValueForKey:keyPP]; + id value = [HDatabaseDAO cleanValue:[self hValueForKey:keyPP]]; return [[self.class dao] remove:[HDatabaseDAO cleanValue:value]]; } - (BOOL)remove:(NSString *)keyppValue diff --git a/Classes/Entity/HDeserializableObject.h b/Classes/Entity/HDeserializableObject.h index d903d3c..f43d2b1 100644 --- a/Classes/Entity/HDeserializableObject.h +++ b/Classes/Entity/HDeserializableObject.h @@ -9,72 +9,44 @@ #import #import "HPropertyMgr.h" #import +#import "NSObject+HDeserializable.h" #pragma mark - defines -//safe get property name -#define pp_name(k) (self.k?@#k:@#k) -#define HPTest(condition, errorInfo) if (!(condition))\ -{\ -self.format_error = [NSString stringWithFormat:@"%@:%@", NSStringFromClass(self.class), errorInfo];\ -NSAssert(NO,self.format_error);\ -return;\ -} -#pragma mark - annotion keys -//ignore property -#define HPIgnore @"ignore" -//property could be nil -#define HPOptional @"optional" -//property map to another key name -#define HPMapto(s) @{@"mapto":s} -//autocast between NSNumber and NSString -#define HPAutoCast @"autocast" -//value scope, only use for NSNumber -#define HPScope(from,to) @{@"scope":@{@"from":@(from),@"to":@(to)}} -//inner type, specified the class in array or the class of dictionary -#define HPInnerType(s) @{@"innertype":s} -//type devide, use on array whose items is of one more type, or use on dictionary whose type if decide by data -//ie. data has a key 'type', if value == 1 , convert itself to Aclass if (value == 2) convert itself to Bclass -//you can write like ppx(@"type", @(1), Aclass, @(2), Bclass) -#define HPDivideType(typekey, type1, class1, ...) @{@"dividetype":@[typekey, type1, class1, __VA_ARGS__]} +@class HDOPropertyExt; - - - - - - - - - - -@interface HDeserializableObject : NSObject +@interface HDeserializableObject : NSObject @property (nonatomic, strong) NSString *format_error; #pragma mark - basic method - +- (instancetype)initWithDictionary:(NSDictionary *)dict; +- (instancetype)initWithDictionary:(NSDictionary *)dict enableKeyMap:(BOOL)enableKeyMap couldEmpty:(BOOL)couldEmpty; //set data with dictionary - (void)setWithDictionary:(NSDictionary *)dict; //enableKeyMap: enable key mapping feature in deserialize progress? - (void)setWithDictionary:(NSDictionary *)dict enableKeyMap:(BOOL)enableKeyMap; +- (void)setWithDictionary:(NSDictionary *)dict enableKeyMap:(BOOL)enableKeyMap couldEmpty:(BOOL)couldEmpty; +//set with anothor obj, just shallow copy +- (void)setWithDObj:(HDeserializableObject *)obj; //before data examlation and value setting, you can pre-processing data there, if some error occured, please record to self.format_error and return nil - (id)preMapValue:(id)value forKey:(NSString *)key; - +//set value to property, you can rewrite it for some special design, if some error occured, please record to self.format_error and return nil +- (void)setValue:(id)value forProperty:(HPropertyDetail *)ppDetail exts:(HDOPropertyExt *)propertyExts enableKeyMap:(BOOL)enableKeyMap couldEmpty:(BOOL)couldEmpty; #pragma mark - advance method //decide the class in array, according to data, propert info and annotation, if some error occured, please record to self.format_error and return nil @@ -82,4 +54,10 @@ return;\ //decide the class of a dictionaty , according to data, propert info and annotation, if some error occured, please record to self.format_error and return nil - (Class)classForDictionary:(NSDictionary *)item ppDetail:(HPropertyDetail *)ppDetail; + +//create obj +- (id)createObjectWithClass:(Class)cls; @end + + + diff --git a/Classes/Entity/HDeserializableObject.m b/Classes/Entity/HDeserializableObject.m index bac114e..e322148 100644 --- a/Classes/Entity/HDeserializableObject.m +++ b/Classes/Entity/HDeserializableObject.m @@ -7,480 +7,195 @@ // #import "HDeserializableObject.h" -/** - * propert extend attr - */ -@interface HDOPropertyExt : NSObject -@property (nonatomic) BOOL isOptional; -@property (nonatomic) BOOL isIgnore; -@property (nonatomic) BOOL isAutocast; -@property (nonatomic) NSString *keyMapto; -@property (nonatomic) Class innerType; -@property (nonatomic) NSArray *divideType; -@property (nonatomic) NSNumber *from; -@property (nonatomic) NSNumber *to; -- (BOOL)isInRange:(NSNumber *)value; -@end -@implementation HDOPropertyExt -- (instancetype)initWithObjs:(id)objs + + + + +@implementation HDeserializableObject +- (instancetype)initWithDictionary:(NSDictionary *)dict +{ + return [self initWithDictionary:dict enableKeyMap:YES couldEmpty:NO]; +} +- (instancetype)initWithDictionary:(NSDictionary *)dict enableKeyMap:(BOOL)enableKeyMap couldEmpty:(BOOL)couldEmpty { self = [super init]; if (self) { - if ([objs isKindOfClass:[NSArray class]]) - { - for (id obj in (NSArray *)objs) - { - [self setWithObj:obj]; - } + NSError *err = [self h_setWithDictionary:dict enableKeyMap:enableKeyMap couldEmpty:couldEmpty]; + if (err) { + self.format_error = err.localizedDescription; } } return self; } -- (void)setWithObj:(id)obj +- (void)setWithDictionary:(NSDictionary *)dict { - if ([obj isKindOfClass:[NSDictionary class]]) + [self setWithDictionary:dict enableKeyMap:YES couldEmpty:NO]; +} +- (void)setWithDictionary:(NSDictionary *)dict enableKeyMap:(BOOL)enableKeyMap +{ + [self setWithDictionary:dict enableKeyMap:enableKeyMap couldEmpty:NO]; +} +- (NSError *)h_setWithDictionary:(NSDictionary *)dict enableKeyMap:(BOOL)enableKeyMap couldEmpty:(BOOL)couldEmpty { + [self setWithDictionary:dict enableKeyMap:enableKeyMap couldEmpty:couldEmpty]; + if (self.format_error) return herr(kDataFormatErrorCode, self.format_error); + else return nil; +} +- (void)setWithDictionary:(NSDictionary *)dict enableKeyMap:(BOOL)enableKeyMap couldEmpty:(BOOL)couldEmpty +{ + NSError *err = [super h_setWithDictionary:dict enableKeyMap:enableKeyMap couldEmpty:couldEmpty]; + if (err) { + self.format_error = err.localizedDescription; + } +} + +- (NSError *)h_setValue:(id)value forProperty:(HPropertyDetail *)ppDetail exts:(HDOPropertyExt *)propertyExts enableKeyMap:(BOOL)enableKeyMap couldEmpty:(BOOL)couldEmpty { + [self setValue:value forProperty:ppDetail exts:propertyExts enableKeyMap:enableKeyMap couldEmpty:couldEmpty]; + if (self.format_error) return herr(kDataFormatErrorCode, self.format_error); + else return nil; +} +- (void)setValue:(id)value forProperty:(HPropertyDetail *)ppDetail exts:(HDOPropertyExt *)propertyExts enableKeyMap:(BOOL)enableKeyMap couldEmpty:(BOOL)couldEmpty { + NSError *err = [super h_setValue:value forProperty:ppDetail exts:propertyExts enableKeyMap:enableKeyMap couldEmpty:couldEmpty]; + if (err) { + self.format_error = err.localizedDescription; + } +} +- (void)setWithDObj:(HDeserializableObject *)obj +{ + if (![obj isKindOfClass:[HDeserializableObject class]]) return; + NSArray *pplist = [[HPropertyMgr shared] entityPropertylist:NSStringFromClass(obj.class) deepTo:[HDeserializableObject class]]; + for (NSString *p in pplist) { - NSDictionary *dict = obj; - NSString *mapTo = dict[@"mapto"]; - if (mapTo) - { - self.keyMapto = mapTo; - return; - } - NSDictionary *scope = dict[@"scope"]; - if (scope) - { - NSNumber *from = scope[@"from"]; - if (from) + id v = [obj valueForKey:p]; + //if has to property + if(v) + { + id oldV = [self valueForKey:p]; + if ([oldV isEqual:v]) continue; + + if([v isKindOfClass:[NSString class]]) { - self.from = from; - self.to = scope[@"to"]; + [self setValue:[v stringValue] forKey:p]; + } + else + { + [self setValue:v forKey:p]; } - return; - } - Class innerType = dict[@"innertype"]; - if (innerType) - { - self.innerType = innerType; - return; - } - - NSArray *divideType = dict[@"dividetype"]; - if (divideType) - { - self.divideType = divideType; } } - else if ([obj isEqualToString:HPIgnore]) - { - self.isIgnore = YES; +} +- (id)h_preMapValue:(id)value forKey:(NSString *)key { + return [self preMapValue:value forKey:key]; +} +- (id)preMapValue:(id)value forKey:(NSString *)key +{ + return value; +} +- (Class)h_classInArray:(id)item ppDetail:(HPropertyDetail *)ppDetail error:(NSError **)error { + Class c = [self classInArray:item ppDetail:ppDetail]; + if (self.format_error) { + *error = herr(kDataFormatErrorCode, self.format_error); + return nil; } - else if ([obj isEqualToString:HPOptional]) - { - self.isOptional = YES; + return c; +} +- (Class)classInArray:(id)item ppDetail:(HPropertyDetail *)ppDetail +{ + NSError *error; + Class c = [super h_classInArray:item ppDetail:ppDetail error:&error]; + if (error) { + self.format_error = error.localizedDescription; + return nil; } - else if ([obj isEqualToString:HPAutoCast]) - { - self.isAutocast = YES; + return c; +} +- (Class)h_classForDictionary:(NSDictionary *)item ppDetail:(HPropertyDetail *)ppDetail error:(NSError * _Nullable __autoreleasing *)error { + Class c = [self classForDictionary:item ppDetail:ppDetail]; + if (self.format_error) { + *error = herr(kDataFormatErrorCode, self.format_error); + return nil; } + return c; } -- (BOOL)isInRange:(NSNumber *)value +- (Class)classForDictionary:(NSDictionary *)item ppDetail:(HPropertyDetail *)ppDetail { - BOOL isInRange = YES; - if (self.from) - { - if ([self.from compare:value] == NSOrderedDescending || [self.to compare:value] == NSOrderedAscending) - { - isInRange = NO; - } + NSError *error; + Class c = [super h_classForDictionary:item ppDetail:ppDetail error:&error]; + if (error) { + self.format_error = error.localizedDescription; + return nil; } - return isInRange; + return c; +} +- (id)h_createObjectWithClass:(Class)cls { + return [self createObjectWithClass:cls]; } -@end - - -@implementation HDeserializableObject -- (void)setWithDictionary:(NSDictionary *)dict -{ - [self setWithDictionary:dict enableKeyMap:YES]; +- (id)createObjectWithClass:(Class)cls { + return [cls new]; } -- (void)setWithDictionary:(NSDictionary *)dict enableKeyMap:(BOOL)enableKeyMap + +#pragma mark - NSCoping +- (id)copyWithZone:(nullable NSZone *)zone { - if (![dict isKindOfClass:[NSDictionary class]]) - { - self.format_error = [NSString stringWithFormat:@"%@ key's value must be a NSDictionary", NSStringFromClass(self.class)]; - return; - } - - NSArray *pplist = [[HPropertyMgr shared] entityPropertyDetailList:NSStringFromClass(self.class) isDepSearch:YES]; - - for (HPropertyDetail *ppDetail in pplist) - { - NSArray *exts = [[self class] annotations:ppDetail.name]; - HDOPropertyExt *propertyExts = [[HDOPropertyExt alloc] initWithObjs:exts]; - if (propertyExts.isIgnore) continue; - - NSString *mappedKey = nil; - if (enableKeyMap) mappedKey = propertyExts.keyMapto; - if (!mappedKey) mappedKey = ppDetail.name; - - id value = [dict valueForKeyPath:mappedKey]; - if (value) + id copy = [[self class] new]; + if (copy) { + NSArray *pplist = [[HPropertyMgr shared] entityPropertylist:NSStringFromClass(self.class) deepTo:[HDeserializableObject class]]; + for (NSString *p in pplist) { - id oldValue = value; - value = [self preMapValue:value forKey:ppDetail.name]; - if (self.format_error) return; - if (!value) value = [NSNull null]; - if ([value isKindOfClass:[NSNull class]]) - { - if (![HPropertyMgr shared].strictModle) continue; - if(!propertyExts.isOptional) - { - self.format_error = [NSString stringWithFormat:@"%@:%@ can not be empty", NSStringFromClass(self.class),ppDetail.name]; - return; - } - } - else if ([value isKindOfClass:[NSString class]]) - { - if (![HPropertyMgr shared].strictModle) - { - [self setValue:[value stringValue] forKey:ppDetail.name]; - } - else - { - if ([ppDetail.typeString isEqualToString:NSStringFromClass([NSString class])] || [ppDetail.typeString isEqualToString:NSStringFromClass([NSMutableString class])]) - { - [self setValue:[value stringValue] forKey:ppDetail.name]; - } - else if (!ppDetail.isObj && propertyExts.isAutocast) - { - //基本类型 - NSNumberFormatter *formatter = [[NSNumberFormatter alloc] init]; - NSNumber *valueNum = [formatter numberFromString:value]; - //if cannot convert value to number , set to 0 by defaylt - if (!valueNum) valueNum = @(0); - if ([propertyExts isInRange:valueNum]) - { - [self setValue:valueNum forKey:ppDetail.name]; - } - else - { - self.format_error = [NSString stringWithFormat:@"%@:%@ value is out of scope (%@, %@)", NSStringFromClass(self.class), ppDetail.name, propertyExts.from, propertyExts.to]; - return; - } - } - else if (ppDetail.isObj && propertyExts.isAutocast && [ppDetail.typeString isEqualToString:NSStringFromClass([NSNumber class])]) - { - //NSNumber - NSNumberFormatter *formatter = [[NSNumberFormatter alloc] init]; - NSNumber *valueNum = [formatter numberFromString:value]; - //if cannot convert value to number , set to 0 by defaylt - if (!valueNum) valueNum = @(0); - if ([propertyExts isInRange:valueNum]) - { - [self setValue:valueNum forKey:ppDetail.name]; - } - else - { - self.format_error = [NSString stringWithFormat:@"%@:%@ value is out of scope (%@, %@)", NSStringFromClass(self.class), ppDetail.name, propertyExts.from, propertyExts.to]; - return; - } - } - else if (ppDetail.isObj && propertyExts.isAutocast && [ppDetail.typeString isEqualToString:NSStringFromClass([NSDate class])]) - { - //NSDate - double date = [value floatValue]; - if ([propertyExts isInRange:@(date)]) - { - [self setValue:[NSDate dateWithTimeIntervalSince1970:date] forKey:ppDetail.name]; - } - else - { - self.format_error = [NSString stringWithFormat:@"%@:%@ value is out of scope (%@, %@)", NSStringFromClass(self.class), ppDetail.name, propertyExts.from, propertyExts.to]; - return; - } - } - else - { - self.format_error = [NSString stringWithFormat:@"%@:%@ must be %c%@ type", NSStringFromClass(self.class), ppDetail.name, ppDetail.typeCode ,ppDetail.typeString]; - return; - } - - } - } - else if ([value isKindOfClass:[NSNumber class]]) + id v = [self valueForKey:p]; + if(v) { - if (![HPropertyMgr shared].strictModle) + + if([v isKindOfClass:[NSString class]]) { - [self setValue:value forKey:ppDetail.name]; + [copy setValue:[[v stringValue] copyWithZone:zone] forKey:p]; } - else + else if ([v isKindOfClass:[NSArray class]]) //default array cannot deep copy { - if (!ppDetail.isObj || [ppDetail.typeString isEqualToString:NSStringFromClass([NSNumber class])]) - { - if ([propertyExts isInRange:value]) - { - [self setValue:value forKey:ppDetail.name]; - } - else - { - self.format_error = [NSString stringWithFormat:@"%@:%@value is out of scope(%@, %@)", NSStringFromClass(self.class), ppDetail.name, propertyExts.from, propertyExts.to]; - return; - } - } - else if (propertyExts.isAutocast && ([ppDetail.typeString isEqualToString:NSStringFromClass([NSString class])] || [ppDetail.typeString isEqualToString:NSStringFromClass([NSMutableString class])])) - { - //NSString - [self setValue:[value stringValue] forKey:ppDetail.name]; - } - else if (propertyExts.isAutocast && ([ppDetail.typeString isEqualToString:NSStringFromClass([NSDate class])])) + NSMutableArray *newArr = [NSMutableArray new]; + for (id o in (NSArray *)v) { - //NSDate - if ([propertyExts isInRange:value]) + if ([o conformsToProtocol:@protocol(NSCopying)]) { - [self setValue:[NSDate dateWithTimeIntervalSince1970:[value doubleValue]] forKey:ppDetail.name]; + [newArr addObject:[o copyWithZone:zone]]; } else { - self.format_error = [NSString stringWithFormat:@"%@:%@value is out of scope(%@, %@)", NSStringFromClass(self.class), ppDetail.name, propertyExts.from, propertyExts.to]; - return; + [newArr addObject:o]; } } - else - { - self.format_error = [NSString stringWithFormat:@"%@:%@ must be %c%@ type", NSStringFromClass(self.class), ppDetail.name, ppDetail.typeCode ,ppDetail.typeString]; - return; - } + [copy setValue:newArr forKey:p]; } - } - else if ([value isKindOfClass:[NSArray class]]) - { - if (!ppDetail.isObj) - { - self.format_error = [NSString stringWithFormat:@"%@:%@ must be %c%@ type", NSStringFromClass(self.class), ppDetail.name, ppDetail.typeCode ,ppDetail.typeString]; - return; - } - if (![ppDetail.typeString isEqualToString:NSStringFromClass([NSArray class])] && - ![ppDetail.typeString isEqualToString:NSStringFromClass([NSMutableArray class])]) - { - self.format_error = [NSString stringWithFormat:@"%@:%@ must be %c%@ type", NSStringFromClass(self.class), ppDetail.name, ppDetail.typeCode ,ppDetail.typeString]; - return; - } - - - NSMutableArray *objs = [NSMutableArray new]; - for (id arrayItem in (NSArray *)value) + else if ([v isKindOfClass:[NSDictionary class]]) { - Class theClass = [self classInArray:arrayItem ppDetail:ppDetail]; - if (self.format_error) return; - if (!theClass) continue; - - if ([theClass isSubclassOfClass:[HDeserializableObject class]]) + NSMutableDictionary *newDict = [NSMutableDictionary new]; + for (id key in (NSDictionary *)v) { - if ([arrayItem isKindOfClass:[NSDictionary class]]) + id o = [(NSDictionary *)v objectForKey:key]; + if ([o conformsToProtocol:@protocol(NSCopying)]) { - NSDictionary *dict2 = arrayItem; - id obj = [[theClass alloc] init]; - [(HDeserializableObject *)obj setWithDictionary:dict2 enableKeyMap:enableKeyMap]; - if ([(HDeserializableObject *)obj format_error]) - { - self.format_error = [(HDeserializableObject *)obj format_error]; - return; - } - else - { - [objs addObject:obj]; - } - + [newDict setObject:[o copyWithZone:zone] forKey:key]; } else { - self.format_error = [NSString stringWithFormat:@"%@:%@ must be NSDictionary type", NSStringFromClass(self.class), ppDetail.name]; - return; + [newDict setObject:o forKey:key]; } } - else - { - [objs addObject:arrayItem]; - } + [copy setValue:newDict forKey:p]; } - [self setValue:objs forKey:ppDetail.name]; - - } - else if ([value isKindOfClass:[NSDictionary class]]) - { - if (!ppDetail.isObj) + else if ([v conformsToProtocol:@protocol(NSCopying)]) { - self.format_error = [NSString stringWithFormat:@"%@:%@ must be %c%@ type", NSStringFromClass(self.class), ppDetail.name, ppDetail.typeCode ,ppDetail.typeString]; - return; + [copy setValue:[v copyWithZone:zone] forKey:p]; } - if ([ppDetail.typeString isEqualToString:NSStringFromClass([NSString class])] || - [ppDetail.typeString isEqualToString:NSStringFromClass([NSNumber class])] || - [ppDetail.typeString isEqualToString:NSStringFromClass([NSArray class])] || - [ppDetail.typeString isEqualToString:NSStringFromClass([NSMutableArray class])]) - { - self.format_error = [NSString stringWithFormat:@"%@:%@ must be %c%@ type", NSStringFromClass(self.class), ppDetail.name, ppDetail.typeCode ,ppDetail.typeString]; - return; - } - - Class theClass = [self classForDictionary:value ppDetail:ppDetail]; - if (self.format_error) return; - - if ([theClass isSubclassOfClass:[NSDictionary class]]) - { - [self setValue:value forKey:ppDetail.name]; - } - else if ([theClass isSubclassOfClass:[HDeserializableObject class]]) - { - id obj = [[theClass alloc] init]; - [(HDeserializableObject *)obj setWithDictionary:value enableKeyMap:enableKeyMap]; - if ([(HDeserializableObject *)obj format_error]) - { - self.format_error = [(HDeserializableObject *)obj format_error]; - return; - } - else - { - [self setValue:obj forKey:ppDetail.name]; - } - } - } - else - { - if (oldValue == value) //value not converted - { - self.format_error = [NSString stringWithFormat:@"%@:%@ is unsupport type %@", NSStringFromClass(self.class), ppDetail.name, NSStringFromClass([value class])]; - return; - } - else //value has converted - { - [self setValue:value forKey:ppDetail.name]; - } - } - } - else - { - if (![HPropertyMgr shared].strictModle) continue; - if(!propertyExts.isOptional) - { - self.format_error = [NSString stringWithFormat:@"%@:%@ can not be empty", NSStringFromClass(self.class), ppDetail.name]; - return; - } - } - } -} -- (id)preMapValue:(id)value forKey:(NSString *)key -{ - return value; -} -- (Class)getClassWithDivideTypeForItem:(id)item propertyExts:(HDOPropertyExt *)propertyExts ppName:(NSString *)ppName -{ - if ([item isKindOfClass:[NSDictionary class]]) - { - if ([propertyExts.divideType count] < 3) - { - self.format_error = [NSString stringWithFormat:@"%@的HNDividedType参数个数至少是3个", ppName]; - return nil; - } - else - { - NSString *key = propertyExts.divideType[0]; - id typeValue = item[key]; - if ([typeValue isKindOfClass:[NSNumber class]]) typeValue = [(NSNumber *)typeValue stringValue]; - Class theClass = nil; - for (int i = 1; i < propertyExts.divideType.count; i += 2) - { - id value = propertyExts.divideType[i]; - if ([value isKindOfClass:[NSNumber class]]) value = [(NSNumber *)value stringValue]; - if ([typeValue isEqual:value]) + else { - if (i + 1 >= propertyExts.divideType.count) - { - self.format_error = [NSString stringWithFormat:@"%@的HNDividedType参数个数错误", ppName]; - return nil; - } - theClass = propertyExts.divideType[i + 1]; + [copy setValue:v forKey:p]; } } - if (theClass) - { - return theClass; - } - else - { - //return nil mean to ignorl the value - return nil; - } - } - } - else - { - self.format_error = [NSString stringWithFormat:@"%@对应的数据并不是一个字典", ppName]; - return nil; - } -} -- (Class)classInArray:(id)item ppDetail:(HPropertyDetail *)ppDetail -{ - if (ppDetail.protocalString) - { - Class theClass = NSClassFromString(ppDetail.protocalString); - return theClass; - } - else - { - HDOPropertyExt *propertyExts = [[HDOPropertyExt alloc] initWithObjs:[[self class] annotations:ppDetail.name]]; - if (propertyExts.innerType) - { - return propertyExts.innerType; - } - else if (propertyExts.divideType) - { - return [self getClassWithDivideTypeForItem:item propertyExts:propertyExts ppName:ppDetail.name]; - } - else - { -// NSAssert(NO, @"you did not specified a type in array"); - return [item class]; - } - } -} -- (Class)classForDictionary:(NSDictionary *)item ppDetail:(HPropertyDetail *)ppDetail -{ - //innerType has highest prioruty - HDOPropertyExt *propertyExts = [[HDOPropertyExt alloc] initWithObjs:[[self class] annotations:ppDetail.name]]; - if (propertyExts.innerType) - { - return propertyExts.innerType; - } - else if (propertyExts.divideType) - { - Class theClass = [self getClassWithDivideTypeForItem:item propertyExts:propertyExts ppName:ppDetail.name]; - if (!theClass) - { - self.format_error = [NSString stringWithFormat:@"can not decide the type of %@", ppDetail.name]; - return nil; - } - else return theClass; - } - else - { - if ([ppDetail.typeString isEqualToString:@""]) - { - NSAssert(NO, @"you did not specified a type of a dictionary"); - return [item class]; - } - else - { - Class theClass = NSClassFromString(ppDetail.typeString); - if (!theClass) - { - self.format_error = [NSString stringWithFormat:@"%@ type is not exsit", ppDetail.typeString]; - return nil; - } - else return theClass; } } + + return copy; } @end diff --git a/Classes/Entity/HEntity.m b/Classes/Entity/HEntity.m index b33b3d6..c52ec25 100644 --- a/Classes/Entity/HEntity.m +++ b/Classes/Entity/HEntity.m @@ -23,7 +23,7 @@ - (NSString *)keyProperty - (void)setWithEntity:(HEntity *)entity { if (![entity isKindOfClass:[HEntity class]]) return; - NSArray *pplist = [[HPropertyMgr shared] entityPropertylist:NSStringFromClass(self.class) isDepSearch:YES]; + NSArray *pplist = [[HPropertyMgr shared] entityPropertylist:NSStringFromClass(self.class) deepTo:[HDeserializableObject class]]; for (NSString *p in pplist) { if ([p isEqualToString:@"ID"] && self.ID == nil) @@ -71,6 +71,7 @@ - (void)hSetValue:(id)value forKey:(NSString *)key } - (id)hValueForKey:(NSString *)key { - return [super valueForKey:key]; + if ([key isEqualToString:@"id"]) return self.ID; + else return [super valueForKey:key]; } @end diff --git a/Classes/Entity/HPropertyMgr.h b/Classes/Entity/HPropertyMgr.h index 4fcca02..5233e6f 100644 --- a/Classes/Entity/HPropertyMgr.h +++ b/Classes/Entity/HPropertyMgr.h @@ -23,16 +23,12 @@ //singleton + (instancetype)shared; - -//default is NO, in strict modle, the example progress is very strict -@property (nonatomic, assign) BOOL strictModle; - //get the cached property list - (NSArray *)entityPropertylist:(NSString *)entityClassName; //deepSearch: should search iteratively by inherit relation -- (NSArray *)entityPropertylist:(NSString *)entityClassName isDepSearch:(BOOL)deepSearch; -- (NSArray *)entityPropertyDetailList:(NSString *)entityClassName isDepSearch:(BOOL)deepSearch; +- (NSArray *)entityPropertylist:(NSString *)entityClassName deepTo:(Class)deepToClass; +- (NSArray *)entityPropertyDetailList:(NSString *)entityClassName deepTo:(Class)deepToClass; @end diff --git a/Classes/Entity/HPropertyMgr.m b/Classes/Entity/HPropertyMgr.m index a1a7798..ddd0691 100644 --- a/Classes/Entity/HPropertyMgr.m +++ b/Classes/Entity/HPropertyMgr.m @@ -8,7 +8,7 @@ #import "HPropertyMgr.h" #import -#import +#import #import "HDeserializableObject.h" @interface HPropertyStructCacheData : NSObject @@ -42,7 +42,6 @@ - (instancetype)init if (self) { _propertyStructCache = [[NSMutableDictionary alloc] init]; _queue = dispatch_queue_create("hpropertymgr.queue", DISPATCH_QUEUE_SERIAL); - _strictModle = YES; } return self; } @@ -50,20 +49,20 @@ - (instancetype)init - (NSArray *)entityPropertylist:(NSString *)entityClassName { - return [self entityPropertylist:entityClassName isDepSearch:NO]; + return [self entityPropertylist:entityClassName deepTo:nil]; } -- (NSArray *)entityPropertylist:(NSString *)entityClassName isDepSearch:(BOOL)deepSearch +- (NSArray *)entityPropertylist:(NSString *)entityClassName deepTo:(Class)deepToClass { __block NSArray *res = nil; dispatch_sync(self.queue, ^{ - res = [self _entityPropertylist:entityClassName isDepSearch:deepSearch]; + res = [self _entityPropertylist:entityClassName deepTo:deepToClass]; }); return res; } -- (NSArray *)_entityPropertylist:(NSString *)entityClassName isDepSearch:(BOOL)deepSearch; +- (NSArray *)_entityPropertylist:(NSString *)entityClassName deepTo:(Class)deepToClass { NSString *key = entityClassName; - if (deepSearch) key = [entityClassName stringByAppendingString:@"nodeep"]; + if (deepToClass) key = [entityClassName stringByAppendingFormat:@"_to_%@", NSStringFromClass(deepToClass?:@"self")]; HPropertyStructCacheData *cacheData = _propertyStructCache[key]; if (!cacheData) @@ -76,7 +75,7 @@ - (NSArray *)_entityPropertylist:(NSString *)entityClassName isDepSearch:(BOOL)d NSMutableArray *pplist = [[NSMutableArray alloc] init]; Class theClass = NSClassFromString(entityClassName); if (!theClass) return nil; - while (theClass != [HDeserializableObject class]) { + while (theClass != deepToClass) { unsigned int count, i; objc_property_t *properties = class_copyPropertyList(theClass, &count); if (count) @@ -85,6 +84,9 @@ - (NSArray *)_entityPropertylist:(NSString *)entityClassName isDepSearch:(BOOL)d { objc_property_t property = properties[i]; NSString *key = [[NSString alloc] initWithCString:property_getName(property) encoding:NSUTF8StringEncoding]; + const char* attr = property_getAttributes(property); + char *vName = strstr(attr, "V_"); //property in category will not be contained + if (vName == NULL) continue; if ([key isEqualToString:@"hash"]) continue; else if ([key isEqualToString:@"superclass"]) continue; else if ([key isEqualToString:@"description"]) continue; @@ -95,7 +97,7 @@ - (NSArray *)_entityPropertylist:(NSString *)entityClassName isDepSearch:(BOOL)d } } free(properties); - if (!deepSearch) break; + if (deepToClass == nil) break; theClass = class_getSuperclass(theClass); } cacheData.pplist = pplist; @@ -103,18 +105,18 @@ - (NSArray *)_entityPropertylist:(NSString *)entityClassName isDepSearch:(BOOL)d return cacheData.pplist; } -- (NSArray *)entityPropertyDetailList:(NSString *)entityClassName isDepSearch:(BOOL)deepSearch +- (NSArray *)entityPropertyDetailList:(NSString *)entityClassName deepTo:(Class)deepToClass { __block NSArray *res = nil; dispatch_sync(self.queue, ^{ - res = [self _entityPropertyDetailList:entityClassName isDepSearch:deepSearch]; + res = [self _entityPropertyDetailList:entityClassName deepTo:deepToClass]; }); return res; } -- (NSArray *)_entityPropertyDetailList:(NSString *)entityClassName isDepSearch:(BOOL)deepSearch +- (NSArray *)_entityPropertyDetailList:(NSString *)entityClassName deepTo:(Class)deepToClass { NSString *key = entityClassName; - if (deepSearch) key = [entityClassName stringByAppendingString:@"nodeep"]; + if (deepToClass) key = [entityClassName stringByAppendingFormat:@"_to_%@", NSStringFromClass(deepToClass?:@"self")]; HPropertyStructCacheData *cacheData = _propertyStructCache[key]; if (!cacheData) { @@ -126,7 +128,7 @@ - (NSArray *)_entityPropertylist:(NSString *)entityClassName isDepSearch:(BOOL)d NSMutableArray *detailList = [NSMutableArray new]; Class theClass = NSClassFromString(entityClassName); if (!theClass) return nil; - NSArray *pplist = [self _entityPropertylist:entityClassName isDepSearch:deepSearch]; + NSArray *pplist = [self _entityPropertylist:entityClassName deepTo:deepToClass]; for (NSString *p in pplist) { //get properties @@ -217,4 +219,4 @@ - (NSArray *)_entityPropertylist:(NSString *)entityClassName isDepSearch:(BOOL)d @implementation HPropertyDetail -@end \ No newline at end of file +@end diff --git a/Classes/Entity/NSObject+HDeserializable.h b/Classes/Entity/NSObject+HDeserializable.h new file mode 100644 index 0000000..9ec013b --- /dev/null +++ b/Classes/Entity/NSObject+HDeserializable.h @@ -0,0 +1,85 @@ +// +// NSObject+HDeserializable.h +// HAccess +// +// Created by zct on 2019/2/15. +// Copyright © 2019 zhangchutian. All rights reserved. +// + +#import +#import "HPropertyMgr.h" +#import +#import + +NS_ASSUME_NONNULL_BEGIN +//safe get property name +#define pp_name(k) (self.k?@#k:@#k) + +#pragma mark - annotion keys + +//ignore property +#define HPIgnore @"ignore" + +//property could be nil +#define HPOptional @"optional" + +//property map to another key name +#define HPMapto(s) @{@"mapto":s} + +//autocast between NSNumber and NSString +#define HPAutoCast @"autocast" + +//value scope, only use for NSNumber +#define HPScope(from,to) @{@"scope":@{@"from":@(from),@"to":@(to)}} + +//inner type, specified the class in array or the class of dictionary +#define HPInnerType(s) @{@"innertype":s} + +//type devide, use on array whose items is of one more type, or use on dictionary whose type if decide by data +//ie. data has a key 'type', if value == 1 , convert itself to Aclass if (value == 2) convert itself to Bclass +//you can write like ppx(@"type", @(1), Aclass, @(2), Bclass) +#define HPDivideType(typekey, type1, class1, ...) @{@"dividetype":@[typekey, type1, class1, __VA_ARGS__]} + + +@class HDOPropertyExt; + +@interface NSObject (HDeserializable) +- (instancetype)h_initWithDictionary:(NSDictionary *)dict error:(NSError **)error; +- (instancetype)h_initWithDictionary:(NSDictionary *)dict enableKeyMap:(BOOL)enableKeyMap couldEmpty:(BOOL)couldEmpty error:(NSError **)error; + +//enableKeyMap: enable key mapping feature in deserialize progress? +- (NSError *)h_setWithDictionary:(NSDictionary *)dict enableKeyMap:(BOOL)enableKeyMap couldEmpty:(BOOL)couldEmpty; + +//before data examlation and value setting, you can pre-processing data there, if some error occured, please record to self.format_error and return nil +- (id)h_preMapValue:(id)value forKey:(NSString *)key; +//set value to property, you can rewrite it for some special design, if some error occured, please record to self.format_error and return nil +- (NSError *)h_setValue:(id)value forProperty:(HPropertyDetail *)ppDetail exts:(HDOPropertyExt *)propertyExts enableKeyMap:(BOOL)enableKeyMap couldEmpty:(BOOL)couldEmpty; +#pragma mark - advance method + +//decide the class in array, according to data, propert info and annotation, if some error occured, please record to self.format_error and return nil +- (Class)h_classInArray:(id)item ppDetail:(HPropertyDetail *)ppDetail error:(NSError **)error; + +//decide the class of a dictionaty , according to data, propert info and annotation, if some error occured, please record to self.format_error and return nil +- (Class)h_classForDictionary:(NSDictionary *)item ppDetail:(HPropertyDetail *)ppDetail error:(NSError **)error; + +//create obj +- (id)h_createObjectWithClass:(Class)cls; +@end + + +/** + * propert extend attr + */ +@interface HDOPropertyExt : NSObject +@property (nonatomic) BOOL isOptional; +@property (nonatomic) BOOL isIgnore; +@property (nonatomic) BOOL isAutocast; +@property (nonatomic) NSString *keyMapto; +@property (nonatomic) Class innerType; +@property (nonatomic) NSArray *divideType; +@property (nonatomic) NSNumber *from; +@property (nonatomic) NSNumber *to; +- (BOOL)isInRange:(NSNumber *)value; +@end + +NS_ASSUME_NONNULL_END diff --git a/Classes/Entity/NSObject+HDeserializable.m b/Classes/Entity/NSObject+HDeserializable.m new file mode 100644 index 0000000..2268801 --- /dev/null +++ b/Classes/Entity/NSObject+HDeserializable.m @@ -0,0 +1,519 @@ +// +// NSObject+HDeserializable.m +// HAccess +// +// Created by zct on 2019/2/15. +// Copyright © 2019 zhangchutian. All rights reserved. +// + +#import "NSObject+HDeserializable.h" + + +@implementation HDOPropertyExt +- (instancetype)initWithObjs:(id)objs +{ + self = [super init]; + if (self) { + if ([objs isKindOfClass:[NSArray class]]) + { + for (id obj in (NSArray *)objs) + { + [self setWithObj:obj]; + } + } + } + return self; +} +- (void)setWithObj:(id)obj +{ + if ([obj isKindOfClass:[NSDictionary class]]) + { + NSDictionary *dict = obj; + NSString *mapTo = dict[@"mapto"]; + if (mapTo) + { + self.keyMapto = mapTo; + return; + } + NSDictionary *scope = dict[@"scope"]; + if (scope) + { + NSNumber *from = scope[@"from"]; + if (from) + { + self.from = from; + self.to = scope[@"to"]; + } + return; + } + Class innerType = dict[@"innertype"]; + if (innerType) + { + self.innerType = innerType; + return; + } + + NSArray *divideType = dict[@"dividetype"]; + if (divideType) + { + self.divideType = divideType; + } + } + else if ([obj isEqualToString:HPIgnore]) + { + self.isIgnore = YES; + } + else if ([obj isEqualToString:HPOptional]) + { + self.isOptional = YES; + } + else if ([obj isEqualToString:HPAutoCast]) + { + self.isAutocast = YES; + } +} +- (BOOL)isInRange:(NSNumber *)value +{ + BOOL isInRange = YES; + if (self.from) + { + if ([self.from compare:value] == NSOrderedDescending || [self.to compare:value] == NSOrderedAscending) + { + isInRange = NO; + } + } + return isInRange; +} +@end + +@implementation NSObject (HDeserializable) +- (instancetype)h_initWithDictionary:(NSDictionary *)dict error:(NSError **)error +{ + id res = [self init]; + if (res) { + NSError *e = [res h_setWithDictionary:dict enableKeyMap:YES couldEmpty:NO]; + if (e) { + *error = e; + return nil; + } + } + return res; +} +- (instancetype)h_initWithDictionary:(NSDictionary *)dict enableKeyMap:(BOOL)enableKeyMap couldEmpty:(BOOL)couldEmpty error:(NSError **)error +{ + id res = [self init]; + if (res) { + NSError *e = [res h_setWithDictionary:dict enableKeyMap:enableKeyMap couldEmpty:couldEmpty]; + if (e) { + *error = e; + return nil; + } + } + return res; +} +- (NSError *)h_setWithDictionary:(NSDictionary *)dict enableKeyMap:(BOOL)enableKeyMap couldEmpty:(BOOL)couldEmpty +{ + if (![dict isKindOfClass:[NSDictionary class]]) + { + NSString *format_error = [NSString stringWithFormat:@"%@ key's value must be a NSDictionary", NSStringFromClass(self.class)]; + return herr(kDataFormatErrorCode, format_error); + } + NSArray *pplist = [[HPropertyMgr shared] entityPropertyDetailList:NSStringFromClass(self.class) deepTo:[NSObject class]]; + + for (HPropertyDetail *ppDetail in pplist) + { + NSArray *exts = [[self class] annotations:ppDetail.name]; + HDOPropertyExt *propertyExts = [[HDOPropertyExt alloc] initWithObjs:exts]; + if (propertyExts.isIgnore) continue; + + NSString *mappedKey = nil; + if (enableKeyMap) mappedKey = propertyExts.keyMapto; + if (!mappedKey) mappedKey = ppDetail.name; + + id value = [dict valueForKeyPath:mappedKey]; + if (!value && mappedKey != ppDetail.name) { + value = [dict valueForKeyPath:ppDetail.name]; + } + if (value) + { + NSError *er = [self h_setValue:value forProperty:ppDetail exts:propertyExts enableKeyMap:enableKeyMap couldEmpty:couldEmpty]; + if (er) return er; + } + else + { + if (couldEmpty) continue; + if(!propertyExts.isOptional) + { + NSString *format_error = [NSString stringWithFormat:@"%@:%@ can not be empty", NSStringFromClass(self.class), ppDetail.name]; + return herr(kDataFormatErrorCode, format_error); + } + } + } + return nil; +} +- (NSError *)h_setValue:(id)value forProperty:(HPropertyDetail *)ppDetail exts:(HDOPropertyExt *)propertyExts enableKeyMap:(BOOL)enableKeyMap couldEmpty:(BOOL)couldEmpty { + id oldValue = value; + value = [self h_preMapValue:value forKey:ppDetail.name]; + if ([value isKindOfClass:[NSError class]]) return value; + if (!value) value = [NSNull null]; + + if ([ppDetail.typeString isEqualToString:NSStringFromClass([NSObject class])]) { //NSObject类型 + [self setValue:value forKey:ppDetail.name]; + return nil; + } + if (ppDetail.typeCode == '@' && ppDetail.typeString.length == 0) { // id这种类型 + [self setValue:value forKey:ppDetail.name]; + return nil; + } + + if ([value isKindOfClass:[NSNull class]]) + { + if(!propertyExts.isOptional) + { + NSString *format_error = [NSString stringWithFormat:@"%@:%@ can not be empty", NSStringFromClass(self.class),ppDetail.name]; + return herr(kDataFormatErrorCode, format_error); + } + } + else if ([value isKindOfClass:[NSString class]]) + { + + if ([ppDetail.typeString isEqualToString:NSStringFromClass([NSString class])] || [ppDetail.typeString isEqualToString:NSStringFromClass([NSMutableString class])]) + { + [self setValue:[value stringValue] forKey:ppDetail.name]; + } + else if (!ppDetail.isObj && propertyExts.isAutocast) + { + //基本类型 + NSNumberFormatter *formatter = [[NSNumberFormatter alloc] init]; + NSNumber *valueNum = [formatter numberFromString:value]; + //if cannot convert value to number , set to 0 by defaylt + if (!valueNum) valueNum = @(0); + if ([propertyExts isInRange:valueNum]) + { + [self setValue:valueNum forKey:ppDetail.name]; + } + else + { + NSString *format_error = [NSString stringWithFormat:@"%@:%@ value is out of scope (%@, %@)", NSStringFromClass(self.class), ppDetail.name, propertyExts.from, propertyExts.to]; + return herr(kDataFormatErrorCode, format_error); + } + } + else if (ppDetail.isObj && propertyExts.isAutocast && [ppDetail.typeString isEqualToString:NSStringFromClass([NSNumber class])]) + { + //NSNumber + NSNumberFormatter *formatter = [[NSNumberFormatter alloc] init]; + NSNumber *valueNum = [formatter numberFromString:value]; + //if cannot convert value to number , set to 0 by defaylt + if (!valueNum) valueNum = @(0); + if ([propertyExts isInRange:valueNum]) + { + [self setValue:valueNum forKey:ppDetail.name]; + } + else + { + NSString *format_error = [NSString stringWithFormat:@"%@:%@ value is out of scope (%@, %@)", NSStringFromClass(self.class), ppDetail.name, propertyExts.from, propertyExts.to]; + return herr(kDataFormatErrorCode, format_error); + } + } + else if (ppDetail.isObj && propertyExts.isAutocast && [ppDetail.typeString isEqualToString:NSStringFromClass([NSDate class])]) + { + //NSDate + double date = [value floatValue]; + if ([propertyExts isInRange:@(date)]) + { + [self setValue:[NSDate dateWithTimeIntervalSince1970:date] forKey:ppDetail.name]; + } + else + { + NSString *format_error = [NSString stringWithFormat:@"%@:%@ value is out of scope (%@, %@)", NSStringFromClass(self.class), ppDetail.name, propertyExts.from, propertyExts.to]; + return herr(kDataFormatErrorCode, format_error); + } + } + else + { + NSString *format_error = [NSString stringWithFormat:@"%@:%@ must be %c%@ type", NSStringFromClass(self.class), ppDetail.name, ppDetail.typeCode ,ppDetail.typeString]; + return herr(kDataFormatErrorCode, format_error); + } + + + } + else if ([value isKindOfClass:[NSNumber class]]) + { + + if (!ppDetail.isObj || [ppDetail.typeString isEqualToString:NSStringFromClass([NSNumber class])]) + { + if ([propertyExts isInRange:value]) + { + [self setValue:value forKey:ppDetail.name]; + } + else + { + NSString *format_error = [NSString stringWithFormat:@"%@:%@value is out of scope(%@, %@)", NSStringFromClass(self.class), ppDetail.name, propertyExts.from, propertyExts.to]; + return herr(kDataFormatErrorCode, format_error); + } + } + else if (propertyExts.isAutocast && ([ppDetail.typeString isEqualToString:NSStringFromClass([NSString class])] || [ppDetail.typeString isEqualToString:NSStringFromClass([NSMutableString class])])) + { + //NSString + [self setValue:[value stringValue] forKey:ppDetail.name]; + } + else if (propertyExts.isAutocast && ([ppDetail.typeString isEqualToString:NSStringFromClass([NSDate class])])) + { + //NSDate + if ([propertyExts isInRange:value]) + { + [self setValue:[NSDate dateWithTimeIntervalSince1970:[value doubleValue]] forKey:ppDetail.name]; + } + else + { + NSString *format_error = [NSString stringWithFormat:@"%@:%@value is out of scope(%@, %@)", NSStringFromClass(self.class), ppDetail.name, propertyExts.from, propertyExts.to]; + return herr(kDataFormatErrorCode, format_error); + } + } + else + { + NSString *format_error = [NSString stringWithFormat:@"%@:%@ must be %c%@ type", NSStringFromClass(self.class), ppDetail.name, ppDetail.typeCode ,ppDetail.typeString]; + return herr(kDataFormatErrorCode, format_error); + } + + } + else if ([value isKindOfClass:[NSArray class]]) + { + if (!ppDetail.isObj) + { + NSString *format_error = [NSString stringWithFormat:@"%@:%@ must be %c%@ type", NSStringFromClass(self.class), ppDetail.name, ppDetail.typeCode ,ppDetail.typeString]; + return herr(kDataFormatErrorCode, format_error); + } + if (![ppDetail.typeString isEqualToString:NSStringFromClass([NSArray class])] && + ![ppDetail.typeString isEqualToString:NSStringFromClass([NSMutableArray class])]) + { + NSString *format_error = [NSString stringWithFormat:@"%@:%@ must be %c%@ type", NSStringFromClass(self.class), ppDetail.name, ppDetail.typeCode ,ppDetail.typeString]; + return herr(kDataFormatErrorCode, format_error); + } + + + NSMutableArray *objs = [NSMutableArray new]; + for (id arrayItem in (NSArray *)value) + { + NSError *err; + Class theClass = [self h_classInArray:arrayItem ppDetail:ppDetail error:&err]; + if (err) return err; + if (!theClass) continue; + + if (theClass == [arrayItem class] || [[arrayItem class] isSubclassOfClass:theClass]) { + [objs addObject:arrayItem]; + } else { + if ([arrayItem isKindOfClass:[NSDictionary class]]) + { + if (theClass == [NSMutableDictionary class]) { + [objs addObject:[arrayItem mutableCopy]]; + } + else { + NSDictionary *dict2 = arrayItem; + id obj = [self h_createObjectWithClass:theClass]; + NSError *err = [obj h_setWithDictionary:dict2 enableKeyMap:enableKeyMap couldEmpty:couldEmpty]; + if (err) + { + return err; + } + else + { + [objs addObject:obj]; + } + } + } + else + { + NSString *format_error = [NSString stringWithFormat:@"%@:%@ must be NSDictionary type", NSStringFromClass(self.class), ppDetail.name]; + return herr(kDataFormatErrorCode, format_error); + } + } + } + [self setValue:objs forKey:ppDetail.name]; + + } + else if ([value isKindOfClass:[NSDictionary class]]) + { + if (!ppDetail.isObj) + { + NSString *format_error = [NSString stringWithFormat:@"%@:%@ must be %c%@ type", NSStringFromClass(self.class), ppDetail.name, ppDetail.typeCode ,ppDetail.typeString]; + return herr(kDataFormatErrorCode, format_error); + } + if ([ppDetail.typeString isEqualToString:NSStringFromClass([NSString class])] || + [ppDetail.typeString isEqualToString:NSStringFromClass([NSNumber class])] || + [ppDetail.typeString isEqualToString:NSStringFromClass([NSArray class])] || + [ppDetail.typeString isEqualToString:NSStringFromClass([NSMutableArray class])]) + { + NSString *format_error = [NSString stringWithFormat:@"%@:%@ must be %c%@ type", NSStringFromClass(self.class), ppDetail.name, ppDetail.typeCode ,ppDetail.typeString]; + return herr(kDataFormatErrorCode, format_error); + } + NSError *err; + Class theClass = [self h_classForDictionary:value ppDetail:ppDetail error:&err]; + if (err) return err; + + if ([value class] == theClass || [[value class] isSubclassOfClass:theClass]) + { + //iOS9会判断不准 + if (theClass == [NSMutableDictionary class]) { + [self setValue:[value mutableCopy] forKey:ppDetail.name]; + } + else { + [self setValue:value forKey:ppDetail.name]; + } + } + else + { + if (theClass == [NSMutableDictionary class]) { + [self setValue:[value mutableCopy] forKey:ppDetail.name]; + } + else { + id obj = [self h_createObjectWithClass:theClass]; + NSError *err = [obj h_setWithDictionary:value enableKeyMap:enableKeyMap couldEmpty:couldEmpty]; + if (err) + { + return err; + } + else + { + [self setValue:obj forKey:ppDetail.name]; + } + } + } + } + else + { + if (oldValue == value) //value not converted + { + NSString *format_error = [NSString stringWithFormat:@"%@:%@ is unsupport type %@", NSStringFromClass(self.class), ppDetail.name, NSStringFromClass([value class])]; + return herr(kDataFormatErrorCode, format_error); + } + else //value has converted + { + [self setValue:value forKey:ppDetail.name]; + } + } + return nil; +} + +- (id)h_preMapValue:(id)value forKey:(NSString *)key +{ + return value; +} +- (Class)h_getClassWithDivideTypeForItem:(id)item propertyExts:(HDOPropertyExt *)propertyExts ppName:(NSString *)ppName error:(NSError **)error +{ + if ([item isKindOfClass:[NSDictionary class]]) + { + if ([propertyExts.divideType count] < 3) + { + NSString *format_error = [NSString stringWithFormat:@"%@的HNDividedType参数个数至少是3个", ppName]; + *error = herr(kDataFormatErrorCode, format_error); + return nil; + } + else + { + NSString *key = propertyExts.divideType[0]; + id typeValue = item[key]; + if ([typeValue isKindOfClass:[NSNumber class]]) typeValue = [(NSNumber *)typeValue stringValue]; + Class theClass = nil; + for (int i = 1; i < propertyExts.divideType.count; i += 2) + { + id value = propertyExts.divideType[i]; + if ([value isKindOfClass:[NSNumber class]]) value = [(NSNumber *)value stringValue]; + if ([typeValue isEqual:value]) + { + if (i + 1 >= propertyExts.divideType.count) + { + NSString *format_error = [NSString stringWithFormat:@"%@的HNDividedType参数个数错误", ppName]; + *error = herr(kDataFormatErrorCode, format_error); + return nil; + } + theClass = propertyExts.divideType[i + 1]; + } + } + if (theClass) + { + return theClass; + } + else + { + //return nil mean to ignorl the value + return nil; + } + } + } + else + { + NSString *format_error = [NSString stringWithFormat:@"%@对应的数据并不是一个字典", ppName]; + *error = herr(kDataFormatErrorCode, format_error); + return nil; + } +} +- (Class)h_classInArray:(id)item ppDetail:(HPropertyDetail *)ppDetail error:(NSError **)error +{ + if (ppDetail.protocalString) + { + Class theClass = NSClassFromString(ppDetail.protocalString); + return theClass; + } + else + { + HDOPropertyExt *propertyExts = [[HDOPropertyExt alloc] initWithObjs:[[self class] annotations:ppDetail.name]]; + if (propertyExts.innerType) + { + return propertyExts.innerType; + } + else if (propertyExts.divideType) + { + return [self h_getClassWithDivideTypeForItem:item propertyExts:propertyExts ppName:ppDetail.name error:error]; + } + else + { + // NSAssert(NO, @"you did not specified a type in array"); + return [item class]; + } + } +} +- (Class)h_classForDictionary:(NSDictionary *)item ppDetail:(HPropertyDetail *)ppDetail error:(NSError **)error +{ + //innerType has highest prioruty + HDOPropertyExt *propertyExts = [[HDOPropertyExt alloc] initWithObjs:[[self class] annotations:ppDetail.name]]; + if (propertyExts.innerType) + { + return propertyExts.innerType; + } + else if (propertyExts.divideType) + { + Class theClass = [self h_getClassWithDivideTypeForItem:item propertyExts:propertyExts ppName:ppDetail.name error:error]; + if (error) return nil; + if (!theClass) + { + NSString *format_error = [NSString stringWithFormat:@"can not decide the type of %@", ppDetail.name]; + *error = herr(kDataFormatErrorCode, format_error); + return nil; + } + else return theClass; + } + else + { + if ([ppDetail.typeString isEqualToString:@""]) + { + NSAssert(NO, @"you did not specified a type of a dictionary"); + return [item class]; + } + else + { + Class theClass = NSClassFromString(ppDetail.typeString); + if (!theClass) + { + NSString *format_error = [NSString stringWithFormat:@"%@ type is not exsit", ppDetail.typeString]; + *error = herr(kDataFormatErrorCode, format_error); + return nil; + } + else return theClass; + } + } +} +- (id)h_createObjectWithClass:(Class)cls { + return [cls new]; +} +@end diff --git a/Classes/Network+Protobuf/HNPBDeseriralizer.h b/Classes/Network+Protobuf/HNPBDeseriralizer.h deleted file mode 100644 index c603f92..0000000 --- a/Classes/Network+Protobuf/HNPBDeseriralizer.h +++ /dev/null @@ -1,16 +0,0 @@ -// -// HNPBDeseriralizer.h -// HAccess -// -// Created by goingta on 16/3/12. -// Copyright © 2016年 zhangchutian. All rights reserved. -// - -#import -#import "HNDeserializer.h" - -@interface HNPBDeseriralizer : NSObject - -+ (instancetype)deserializerWithClass:(Class)aClass; - -@end diff --git a/Classes/Network+Protobuf/HNPBDeseriralizer.m b/Classes/Network+Protobuf/HNPBDeseriralizer.m deleted file mode 100644 index 176340a..0000000 --- a/Classes/Network+Protobuf/HNPBDeseriralizer.m +++ /dev/null @@ -1,64 +0,0 @@ -// -// HNPBDeseriralizer.m -// HAccess -// -// Created by goingta on 16/3/12. -// Copyright © 2016年 zhangchutian. All rights reserved. -// - -#import "HNPBDeseriralizer.h" -#import -#import -#import - -@interface HNPBDeseriralizer () -@property (nonatomic) Class entityClass; -@end - -@implementation HNPBDeseriralizer - -@synthesize deserializeKeyPath; -+ (instancetype)deserializerWithClass:(Class)aClass -{ - HNPBDeseriralizer *obj = [self new]; - obj.entityClass = aClass; - return obj; -} - -- (id)preprocess:(id)rudeData -{ - if (![rudeData isKindOfClass:[NSData class]]) - { - NSString *errorMsg = [NSString stringWithFormat:@"HNPBDeseriralizer: need NSData as input but your data is '%@'", NSStringFromClass([rudeData class])]; - return herr(kInnerErrorCode, errorMsg); - } - - return rudeData; -} - -- (id)deserialization:(id)rudeData -{ - if(self.entityClass == NULL) - { - NSString *errorMsg = [NSString stringWithFormat:@"%s, can't get the entity class", __FUNCTION__]; - return herr(kInnerErrorCode, errorMsg); - } - - NSError *err = nil; - - id entity = [self.entityClass parseFromData:rudeData error:&err]; - - if (err) - { - return [NSError errorWithDomain:@"com.haccess.HNPBDeseriralizer" code:kDataFormatErrorCode description:err.description]; - } - - return entity; -} - -- (NSString *)mockFileType -{ - return @"pb"; -} - -@end diff --git a/Classes/Network/HNCustomCacheStrategy.h b/Classes/Network/HNCustomCacheStrategy.h index 19a3435..4099973 100644 --- a/Classes/Network/HNCustomCacheStrategy.h +++ b/Classes/Network/HNCustomCacheStrategy.h @@ -9,7 +9,7 @@ #import #import "HNCacheStrategy.h" -typedef void(^HNCustomCacheCallback)(BOOL shouldRequest, NSData *cachedData); +typedef void(^HNCustomCacheCallback)(BOOL shouldContinue, NSData *cachedData); /** @@ -20,8 +20,10 @@ typedef void(^HNCustomCacheCallback)(BOOL shouldRequest, NSData *cachedData); @property (nonatomic) NSString *cacheKey; //how long the cache lives, default is a week @property (nonatomic) long long cacheDuration; -//excute cache logic +//excute cache logic before request - (void)cacheLogic:(HNCustomCacheCallback)cacheCallback; +//excute cache logic on request fail +- (void)cacheLogicOnFail:(NSError *)error cacheCallback:(HNCustomCacheCallback)cacheCallback; //handle response, mostly we just write cache - (NSData *)handleRespInfo:(NSData *)respInfo; //is my cache usable, if not exist or cache is too old return NO @@ -54,5 +56,13 @@ typedef void(^HNCustomCacheCallback)(BOOL shouldRequest, NSData *cachedData); * else send request and save a cache if success */ @interface HNCacheTypeAlternative : HNCustomCacheStrategy -+ (instancetype)createWtihCacheDuration:(long long)cacheDuration nextRequstInterval:(long long)nextRequestInterval; -@end \ No newline at end of file ++ (instancetype)createWtihNextRequstInterval:(long long)nextRequestInterval; +@end + + +/** + * When the request fails, if there is a cache, first you will get cached response for success block, then you will get a fail block + */ +@interface HNCacheTypeUseCacheOnFail : HNCustomCacheStrategy ++ (instancetype)createWtihCacheDuration:(long long)cacheDuration; +@end diff --git a/Classes/Network/HNCustomCacheStrategy.m b/Classes/Network/HNCustomCacheStrategy.m index 5852b0c..fc6d1d8 100644 --- a/Classes/Network/HNCustomCacheStrategy.m +++ b/Classes/Network/HNCustomCacheStrategy.m @@ -51,7 +51,10 @@ - (void)setCacheDuration:(long long)cacheDuration } - (void)cacheLogic:(HNCustomCacheCallback)cacheCallback { - + cacheCallback(YES, nil); +} +- (void)cacheLogicOnFail:(NSError *)error cacheCallback:(HNCustomCacheCallback)cacheCallback { + cacheCallback(YES, nil); } - (NSData *)handleRespInfo:(id)respInfo { @@ -108,10 +111,10 @@ @interface HNCacheTypeAlternative () @end @implementation HNCacheTypeAlternative -+ (instancetype)createWtihCacheDuration:(long long)cacheDuration nextRequstInterval:(long long)nextRequestInterval ++ (instancetype)createWtihNextRequstInterval:(long long)nextRequestInterval { HNCacheTypeAlternative *cacheType = [HNCacheTypeAlternative new]; - cacheType.cacheDuration = cacheDuration; + cacheType.cacheDuration = nextRequestInterval; cacheType.requstInterval = nextRequestInterval; return cacheType; } @@ -135,4 +138,24 @@ - (void)cacheLogic:(HNCustomCacheCallback)cacheCallback } cacheCallback((data == nil), data); } -@end \ No newline at end of file +@end + + + +@implementation HNCacheTypeUseCacheOnFail ++ (instancetype)createWtihCacheDuration:(long long)cacheDuration +{ + HNCacheTypeUseCacheOnFail *cacheType = [HNCacheTypeUseCacheOnFail new]; + cacheType.cacheDuration = cacheDuration; + return cacheType; +} +- (void)cacheLogicOnFail:(NSError *)error cacheCallback:(HNCustomCacheCallback)cacheCallback +{ + NSData *data = nil; + if ([self isCacheUseable:self.cacheKey]) + { + data = [[HFileCache shareCache] dataForKey:self.cacheKey]; + } + cacheCallback(NO, data); +} +@end diff --git a/Classes/Network/HNJsonDeserializer.m b/Classes/Network/HNJsonDeserializer.m index 9393b02..849f53d 100644 --- a/Classes/Network/HNJsonDeserializer.m +++ b/Classes/Network/HNJsonDeserializer.m @@ -19,7 +19,7 @@ - (id)preprocess:(id)rudeData NSString *errorMsg = [NSString stringWithFormat:@"HNJsonDeserializer: need NSData as input but your data is '%@'", NSStringFromClass([rudeData class])]; return herr(kDataFormatErrorCode, errorMsg); } - id jsonValue = [rudeData JSONValue]; + id jsonValue = [rudeData h_JSONValue]; if (!jsonValue) { return [NSError errorWithDomain:@"com.haccess.HNJsonDeserializer" @@ -77,20 +77,11 @@ - (id)deserialization:(id)rudeData return herr(kInnerErrorCode, errorMsg); } //create entity - - HDeserializableObject *entity = (HDeserializableObject *)[[self.entityClass alloc]init]; - if (![entity isKindOfClass:[HDeserializableObject class]]) - { - - NSString *errorMsg = [NSString stringWithFormat:@"%s, %@ is not a subclass of HDeserializableObject", __FUNCTION__, NSStringFromClass(self.entityClass)]; - return herr(kInnerErrorCode, errorMsg); - } - - - [entity setWithDictionary:rudeData]; - if (entity.format_error) + NSObject *entity = [[self.entityClass alloc] init]; + NSError * err = [entity h_setWithDictionary:rudeData enableKeyMap:YES couldEmpty:NO]; + if (err) { - return [NSError errorWithDomain:@"com.haccess.HNJsonDeserializer.HNEntityDeserializer" code:kDataFormatErrorCode description:entity.format_error]; + return err; } else return entity; @@ -139,18 +130,11 @@ - (id)deserialization:(id)rudeData return herr(kInnerErrorCode, errorMsg); } - if (![targetClass isSubclassOfClass:[HDeserializableObject class]]) - { - NSString *errorMsg = [NSString stringWithFormat:@"%@: is not subclass of HDeserializableObject", NSStringFromClass(self.class)]; - return herr(kInnerErrorCode, errorMsg); - } - - HDeserializableObject *entity = (HDeserializableObject *)[[targetClass alloc]init]; - [entity setWithDictionary:dict]; - if (entity.format_error) + NSObject *entity = [[targetClass alloc] init]; + NSError * err = [entity h_setWithDictionary:dict enableKeyMap:YES couldEmpty:NO]; + if (err) { - NSString *errInfo = [NSString stringWithFormat:@"%@:%@", NSStringFromClass(self.class), entity.format_error]; - return [NSError errorWithDomain:@"com.haccess.HNJsonDeserializer.HNArrayDeserializer" code:kDataFormatErrorCode description:errInfo]; + return err; } [res addObject:entity]; } @@ -178,4 +162,4 @@ - (id)deserialization:(id)rudeData if (self.block) return self.block(rudeData); else return nil; } -@end \ No newline at end of file +@end diff --git a/Classes/Network/HNProvider_AF2.h b/Classes/Network/HNProvider_AF2.h deleted file mode 100644 index 8a58553..0000000 --- a/Classes/Network/HNProvider_AF2.h +++ /dev/null @@ -1,14 +0,0 @@ -// -// HNProvider_AF2.h -// HAccess -// -// Created by zhangchutian on 16/3/21. -// Copyright © 2016年 zhangchutian. All rights reserved. -// - -#import -#import "HNetworkProvider.h" - -@interface HNProvider_AF2 : NSObject - -@end diff --git a/Classes/Network/HNProvider_AF2.m b/Classes/Network/HNProvider_AF2.m deleted file mode 100644 index dc9b5f1..0000000 --- a/Classes/Network/HNProvider_AF2.m +++ /dev/null @@ -1,217 +0,0 @@ -// -// HNProvider_AF2.m -// HAccess -// -// Created by zhangchutian on 16/3/21. -// Copyright © 2016年 zhangchutian. All rights reserved. -// - -#import "HNProvider_AF2.h" -#import "HNQueueManager.h" -#import "HNetworkMultiDataObj.h" -#import -#import - -@interface HNProvider_AF2 () -@property (nonatomic) NSOperation *myOperation; -@end - -@implementation HNProvider_AF2 -@synthesize urlString; -@synthesize params; -@synthesize method; -@synthesize queueName; - -@synthesize timeoutInterval; -@synthesize shouldContinueInBack; -@synthesize fileDownloadPath; -@synthesize headParameters; -@synthesize cachePolicy; - -@synthesize successCallback; -@synthesize failCallback; -@synthesize progressCallback; -@synthesize willSendCallback; - - -HReg(HNetworkProviderRegKey) - - - -static dispatch_queue_t HNProviderProcessingQueue() { - static dispatch_queue_t HNProviderProcessingQueue; - static dispatch_once_t onceToken; - dispatch_once(&onceToken, ^{ - HNProviderProcessingQueue = dispatch_queue_create("com.hnetwork.processing", DISPATCH_QUEUE_CONCURRENT); - }); - - return HNProviderProcessingQueue; -} - -- (instancetype)init -{ - self = [super init]; - if (self) { - self.cachePolicy = NSURLRequestReloadIgnoringLocalCacheData; - } - return self; -} - -- (NSOperation *)sendRequest -{ - NSMutableDictionary* parametersDict = nil; - NSMutableDictionary* multiDataDict = nil; - if ([self.params isKindOfClass:[NSDictionary class]]) - { - parametersDict = [NSMutableDictionary new]; - multiDataDict = [NSMutableDictionary new]; - NSMutableDictionary *rudeParam = self.params; - for (NSString *key in rudeParam) - { - id value = rudeParam[key]; - if ([value isKindOfClass:[HNetworkMultiDataObj class]]) - { - [multiDataDict setObject:value forKey:key]; - } - else - { - [parametersDict setObject:value forKey:key]; - } - } - } - - AFHTTPRequestSerializer *requestSerializer = [AFHTTPRequestSerializer serializer]; - //timeout - requestSerializer.timeoutInterval = self.timeoutInterval; - //HEAD - for (NSString *key in self.headParameters) - { - [requestSerializer setValue:self.headParameters[key] forHTTPHeaderField:key]; - } - //2.get an Operation - NSMutableURLRequest* request = nil; - //multi Data - if(multiDataDict.allKeys.count > 0) - { - request = [requestSerializer multipartFormRequestWithMethod:@"POST" URLString:urlString parameters:parametersDict constructingBodyWithBlock:^(id multipartFormData) - { - //get file - for(NSString* key in multiDataDict.allKeys) - { - HNetworkMultiDataObj* multiDataObj = [multiDataDict valueForKey:key]; - if(multiDataObj.datas) - { - //datas - NSString* newKey = [NSString stringWithFormat:@"%@[]",key]; - for(NSData* data in multiDataObj.datas) - { - [multipartFormData appendPartWithFileData:data name:newKey fileName:multiDataObj.fileName mimeType:multiDataObj.mimeType]; - } - } - else if(multiDataObj.data) - { - //data - [multipartFormData appendPartWithFileData:multiDataObj.data name:key fileName:multiDataObj.fileName mimeType:multiDataObj.mimeType]; - } - else - { - //url - if(multiDataObj.filePath == nil) - { - NSLog(@"%s:file not found", __FUNCTION__); - continue ; - } - //is filePath validate - if(![[NSFileManager defaultManager] fileExistsAtPath:multiDataObj.filePath]) - { - NSLog(@"%s: file not found", __FUNCTION__); - continue; - } - - NSURL* url = [NSURL fileURLWithPath:multiDataObj.filePath]; - NSError* error = nil; - BOOL isFailed = [multipartFormData appendPartWithFileURL:url name:key fileName:multiDataObj.fileName mimeType:multiDataObj.mimeType error:&error]; - if(isFailed) - { - NSLog(@"multiDataError:%@", error.localizedDescription); - } - } - } - }error:nil]; - } - else - { - request = [requestSerializer requestWithMethod:self.method URLString:urlString parameters:parametersDict error:nil]; - } - - //set to body directly - if ([self.params isKindOfClass:[NSData class]]) - { - request.HTTPBody = self.params; - } - request.cachePolicy = self.cachePolicy; - //print param - NSMutableString *paramString = [NSMutableString new]; - for (NSString *key in parametersDict) - { - if (paramString.length > 0) [paramString appendFormat:@"&"]; - [paramString appendFormat:@"%@=%@", key, [parametersDict[key] stringValue]]; - } - - NSLog(@" "); - NSLog(@"#### send request"); - NSLog(@"#### %@ %@",self.method, urlString); - if (paramString.length > 0) - { - NSLog(@"#### param: %@", paramString); - } - if (multiDataDict.count > 0) - { - NSLog(@"#### multiData: %@", paramString); - } - NSLog(@" "); - - __weak typeof(self) weakSelf = self; - AFHTTPRequestOperation *operation = [[AFHTTPRequestOperation alloc] initWithRequest:request]; - operation.completionQueue = HNProviderProcessingQueue(); - [operation setCompletionBlockWithSuccess:^(AFHTTPRequestOperation * _Nonnull operation, id _Nonnull responseObject) { - - if (weakSelf.successCallback) weakSelf.successCallback(operation, operation.response, responseObject); - - } failure:^(AFHTTPRequestOperation * _Nonnull operation, NSError * _Nonnull error) { - - if (weakSelf.failCallback) weakSelf.failCallback(operation, error); - - }]; - - self.myOperation = operation; - - if (self.fileDownloadPath) - { - [operation setDownloadProgressBlock:self.progressCallback]; - [operation setOutputStream:[NSOutputStream outputStreamToFileAtPath:self.fileDownloadPath append:NO]]; - } - else if (multiDataDict.count > 0) - { - [operation setUploadProgressBlock:self.progressCallback]; - } - - if (self.shouldContinueInBack) - { - [operation setShouldExecuteAsBackgroundTaskWithExpirationHandler:nil]; - } - - if (self.willSendCallback) self.willSendCallback(request); - - NSOperationQueue* operataionQueue = nil; - if(queueName) operataionQueue = [[HNQueueManager instance] getOperationQueueWithName:queueName]; - else operataionQueue = [HNQueueManager instance].globalQueue; - [operataionQueue addOperation:operation]; - - return operation; -} -- (void)cancel -{ - [self.myOperation cancel]; -} -@end diff --git a/Classes/Network/HNProvider_AF3.h b/Classes/Network/HNProvider_AF3.h new file mode 100644 index 0000000..93c558b --- /dev/null +++ b/Classes/Network/HNProvider_AF3.h @@ -0,0 +1,27 @@ +// +// HNProvider_AF3.h +// HAccess +// +// Created by zct on 2017/5/22. +// Copyright © 2017年 zhangchutian. All rights reserved. +// + +#import +#import "HNetworkProvider.h" +@interface HNProvider_AF3 : NSObject +@property (nonatomic) NSString *urlString; +@property (nonatomic) id params; +@property (nonatomic) NSString *method; +@property (nonatomic) NSString *queueName; +@property (nonatomic) NSString *requstContentType; +@property (nonatomic) NSTimeInterval timeoutInterval; +@property (nonatomic) BOOL shouldContinueInBack; +@property (nonatomic) NSString *fileDownloadPath; +@property (nonatomic) NSDictionary *headParameters; +@property (nonatomic) NSURLRequestCachePolicy cachePolicy; + +@property (nonatomic, strong) HNPSuccessCallback successCallback; +@property (nonatomic, strong) HNPFailCallback failCallback; +@property (nonatomic, strong) HNProgressBlock progressCallback; +@property (nonatomic, strong) HNPWillSendCallback willSendCallback; +@end diff --git a/Classes/Network/HNProvider_AF3.m b/Classes/Network/HNProvider_AF3.m new file mode 100644 index 0000000..cb4494c --- /dev/null +++ b/Classes/Network/HNProvider_AF3.m @@ -0,0 +1,281 @@ +// +// HNProvider_AF3.m +// HAccess +// +// Created by zct on 2017/5/22. +// Copyright © 2017年 zhangchutian. All rights reserved. +// + +#import "HNProvider_AF3.h" +#import +#import +#import +#import +#import +#import "HNetworkMultiDataObj.h" +#import "HNQueueManager.h" + +@interface HNProvider_AF3 () +@property (nonatomic) NSURLSessionTask *myTask; +@property (nonatomic) dispatch_queue_t queue; +@end + +@implementation HNProvider_AF3 + + + +HReg(HNetworkProviderRegKey) + + + +- (instancetype)init +{ + self = [super init]; + if (self) { + self.cachePolicy = NSURLRequestReloadIgnoringLocalCacheData; + self.queue = hCreateQueue("com.hnetwork.processing", DISPATCH_QUEUE_SERIAL); + } + return self; +} ++ (AFHTTPResponseSerializer *)responseSerializer +{ + AFHTTPResponseSerializer *serializer = [AFHTTPResponseSerializer serializer]; + serializer.acceptableContentTypes = [NSSet setWithObjects: + @"application/json", + @"text/json", + @"text/javascript", + @"text/html", + @"text/plain", + @"application/atom+xml", + @"application/xml", + @"text/xml", + @"image/png", + @"image/jpeg", nil]; + return serializer; +} +- (AFHTTPSessionManager *)sessionManager { + + // if (!self.shouldContinueInBack) + if (1) + { + static AFHTTPSessionManager *manager1; + static dispatch_once_t onceToken1; + dispatch_once(&onceToken1, ^{ + NSURLSessionConfiguration *configuration = [NSURLSessionConfiguration defaultSessionConfiguration]; + configuration.timeoutIntervalForRequest = 30; + manager1 = [[AFHTTPSessionManager alloc] initWithSessionConfiguration:configuration]; + manager1.responseSerializer = [HNProvider_AF3 responseSerializer]; + + }); + return manager1; + } + else + { + static AFHTTPSessionManager *manager2; + static dispatch_once_t onceToken2; + dispatch_once(&onceToken2, ^{ + NSURLSessionConfiguration *configuration = [NSURLSessionConfiguration backgroundSessionConfiguration:@"com.hnetwork.provider"]; + configuration.timeoutIntervalForRequest = 30; + manager2 = [[AFHTTPSessionManager alloc] initWithSessionConfiguration:configuration]; + AFHTTPResponseSerializer *serializer = [AFHTTPResponseSerializer serializer]; + manager2.responseSerializer = [HNProvider_AF3 responseSerializer]; + }); + return manager2; + } +} + + +- (NSURLSessionTask *)sendRequest +{ + syncAtQueue(self.queue, ^{ + NSMutableDictionary* parametersDict = nil; + NSMutableDictionary* multiDataDict = nil; + if ([self.params isKindOfClass:[NSDictionary class]]) + { + parametersDict = [NSMutableDictionary new]; + multiDataDict = [NSMutableDictionary new]; + NSMutableDictionary *rudeParam = self.params; + for (NSString *key in rudeParam) + { + id value = rudeParam[key]; + if ([value isKindOfClass:[HNetworkMultiDataObj class]]) + { + [multiDataDict setObject:value forKey:key]; + } + else + { + [parametersDict setObject:[value serialization] forKey:key]; + } + } + } + AFHTTPRequestSerializer *requestSerializer = nil; + if ([self.requstContentType.lowercaseString isEqualToString:@"application/json"]) { + requestSerializer = [AFJSONRequestSerializer serializerWithWritingOptions:NSJSONWritingPrettyPrinted]; + } + else { + requestSerializer = [AFHTTPRequestSerializer serializer]; + } + //timeout + requestSerializer.timeoutInterval = self.timeoutInterval; + //HEAD + for (NSString *key in self.headParameters) + { + [requestSerializer setValue:self.headParameters[key] forHTTPHeaderField:key]; + } + //2.get an Operation + NSMutableURLRequest* request = nil; + + @weakify(self) + //multi Data + if(multiDataDict.allKeys.count > 0) + { + + request = [requestSerializer multipartFormRequestWithMethod:@"POST" URLString:self.urlString parameters:parametersDict constructingBodyWithBlock:^(id multipartFormData) + { + @strongify(self) + //get file + for(NSString* key in multiDataDict.allKeys) + { + HNetworkMultiDataObj* multiDataObj = [multiDataDict valueForKey:key]; + if(multiDataObj.datas) + { + //datas + for(NSData* data in multiDataObj.datas) + { + [multipartFormData appendPartWithFileData:data name:key fileName:multiDataObj.fileName mimeType:multiDataObj.mimeType]; + } + } + else if(multiDataObj.data) + { + //data + [multipartFormData appendPartWithFileData:multiDataObj.data name:key fileName:multiDataObj.fileName mimeType:multiDataObj.mimeType]; + } + else + { + //url + if(multiDataObj.filePath == nil) + { + NSLog(@"%s:file not found", __FUNCTION__); + continue ; + } + //is filePath validate + if(![[NSFileManager defaultManager] fileExistsAtPath:multiDataObj.filePath]) + { + NSLog(@"%s: file not found", __FUNCTION__); + continue; + } + + NSURL* url = [NSURL fileURLWithPath:multiDataObj.filePath]; + NSError* error = nil; + BOOL isFailed = [multipartFormData appendPartWithFileURL:url name:key fileName:multiDataObj.fileName mimeType:multiDataObj.mimeType error:&error]; + if(isFailed) + { + NSLog(@"multiDataError:%@", error.localizedDescription); + } + } + } + }error:nil]; + } + else + { + request = [requestSerializer requestWithMethod:self.method URLString:self.urlString parameters:parametersDict error:nil]; + } + + //set to body directly + if ([self.params isKindOfClass:[NSData class]]) + { + request.HTTPBody = self.params; + } + request.cachePolicy = self.cachePolicy; + + + //print param + NSMutableString *paramString = [NSMutableString new]; + for (NSString *key in parametersDict) + { + if (paramString.length > 0) [paramString appendFormat:@"&"]; + [paramString appendFormat:@"%@=%@", key, parametersDict[key]]; + } + + NSLog(@"\n\n#### send request:\n%@ %@\n%@",self.method, self.urlString, paramString.length>0?paramString:@""); + if (multiDataDict.count > 0) + { + NSLog(@"\n\n#### multiData: %@", paramString); + } + + if (self.willSendCallback) self.willSendCallback(request); + NSURLSessionTask *task = [self requestTask:request progress:^(NSProgress * _Nullable progress) { + + @strongify(self) + dispatch_async(dispatch_get_main_queue(), ^{ + if (self.progressCallback) self.progressCallback(self, progress.fractionCompleted); + }); + + } completion:^(NSURLResponse *response, id responseObject, NSError *error) { + asyncAtQueue(self.queue, ^{ + @strongify(self) + if (!error) + { + if (self.successCallback) self.successCallback(self, response, responseObject); + } + else + { + if (self.failCallback) self.failCallback(self, response, error); + } + if (self.myTask) + { + [[NSNotificationCenter defaultCenter] postNotificationName:HNQueueTaskFinishNotification object:nil userInfo:@{@"data":self.myTask, @"queue":self.queueName?:@"global"}]; + self.myTask = nil; + } + + }); + }]; + + self.myTask = task; + + HNQueue *operataionQueue; + if(self.queueName) operataionQueue = [[HNQueueManager instance] getOperationQueueWithName:self.queueName]; + else operataionQueue = [HNQueueManager instance].globalQueue; + [operataionQueue addTask:self.myTask]; + }); + return self.myTask; +} +- (NSURLSessionTask *)requestTask:(NSMutableURLRequest *)request progress:(nullable void (^)(NSProgress *downloadProgress))progressBlock completion:(void (^)(NSURLResponse *response, id responseObject, NSError *error))completion { + + __block NSURLSessionTask *task = nil; + + if (self.fileDownloadPath) + { + @weakify(self) + task = [[self sessionManager] downloadTaskWithRequest:request progress:progressBlock destination:^NSURL *(NSURL *targetPath, NSURLResponse *response) { + @strongify(self) + return [NSURL fileURLWithPath:self.fileDownloadPath]; + } completionHandler:completion]; + } + else + { + if ([self.method isEqualToString:@"POST"]) + { + task = [[self sessionManager] uploadTaskWithStreamedRequest:request + progress:progressBlock + completionHandler:completion]; + } + else { + task = [[self sessionManager] dataTaskWithRequest:request uploadProgress:nil downloadProgress:nil completionHandler:completion]; + } + } + + return task; +} +- (void)cancel +{ + syncAtQueue(self.queue, ^{ + if (self.myTask) + { + [[NSNotificationCenter defaultCenter] postNotificationName:HNQueueTaskFinishNotification object:nil userInfo:@{@"data":self.myTask, @"queue":self.queueName?:@"global"}]; + [self.myTask cancel]; + self.myTask = nil; + } + }); +} +@end diff --git a/Classes/Network/HNQueueManager.h b/Classes/Network/HNQueueManager.h index 09e6ffb..1f2ad80 100644 --- a/Classes/Network/HNQueueManager.h +++ b/Classes/Network/HNQueueManager.h @@ -7,6 +7,16 @@ // #import +#import +#define HNQueueTaskFinishNotification @"HNQueueTaskFinishNotification" + +@interface HNQueue : NSObject +@property (nonatomic, readonly)NSInteger maxConcurrentCount; +@property (nonatomic) simple_callback emptyCallback; + +- (void)addTask:(NSURLSessionTask *)task; +- (void)cancelAllTask; +@end /* * this is a queue manager @@ -16,12 +26,14 @@ //singleton + (instancetype)instance; -@property (nonatomic, readonly) NSOperationQueue* globalQueue; +@property (nonatomic, readonly) HNQueue* globalQueue; // get special queue -- (NSOperationQueue*)getOperationQueueWithName:(NSString*)name; +- (HNQueue*)getOperationQueueWithName:(NSString*)name; // distroy queue -- (void)destoryOperationQueueWithName:(NSString*)name; ++ (void)destoryOperationQueueWithName:(NSString*)name; // init queue manually + (void)initQueueWithName:(NSString *)queueName maxMaxConcurrent:(NSInteger)maxMaxConcurrent; +// queue finish callback ++ (void)queue:(NSString *)queueName finish:(simple_callback)finish; @end diff --git a/Classes/Network/HNQueueManager.m b/Classes/Network/HNQueueManager.m index 5506f9d..8ba39a0 100644 --- a/Classes/Network/HNQueueManager.m +++ b/Classes/Network/HNQueueManager.m @@ -7,10 +7,104 @@ // #import "HNQueueManager.h" +#import +#import + +@interface HNQueue () +@property (nonatomic) NSString *name; +@property (nonatomic) NSInteger concurrent; +@property (nonatomic) dispatch_queue_t dataQueue; +@property (nonatomic) NSMutableArray *waitingPool; +@property (nonatomic) NSMutableArray *runingPool; +@end + +@implementation HNQueue + +- (instancetype)initWithConcurrent:(NSInteger)concurrent name:(NSString *)name +{ + self = [super init]; + if (self) { + _concurrent = concurrent; + NSString *queueName = [NSString stringWithFormat:@"com.hnetwork.hnqueue.%@", name]; + char *queueNameStr = (char *)[queueName cStringUsingEncoding:NSUTF8StringEncoding]; + _dataQueue = hCreateQueue(queueNameStr, DISPATCH_QUEUE_SERIAL); + _name = name; + _waitingPool = [NSMutableArray new]; + _runingPool = [NSMutableArray new]; + [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(taskFinish:) name:HNQueueTaskFinishNotification object:nil]; + } + return self; +} +- (void)dealloc +{ + [[NSNotificationCenter defaultCenter] removeObserver:self]; +} +- (void)addTask:(NSURLSessionTask *)task +{ + asyncAtQueue(self.dataQueue, ^{ + [self.waitingPool addObject:task]; + [self flushWaitingPool]; + }); +} +- (void)cancelAllTask +{ + asyncAtQueue(self.dataQueue, ^{ + for (NSURLSessionTask *task in self.waitingPool) + { + [task cancel]; + } + [self.waitingPool removeAllObjects]; + for (NSURLSessionTask *task in self.runingPool) + { + [task cancel]; + } + [self.runingPool removeAllObjects]; + }); +} + +- (void)taskFinish:(NSNotification *)noti +{ + NSString *queueName = noti.userInfo[@"queue"]; + if (![queueName isEqual:self.name]) return; + NSURLSessionTask *targetTask = noti.userInfo[@"data"]; + if (!targetTask) return; + asyncAtQueue(self.dataQueue, ^{ + [self.runingPool removeObject:targetTask]; + [self.waitingPool removeObject:targetTask]; + if (self.runingPool.count == 0 && self.waitingPool.count == 0) + { + if (self.emptyCallback) self.emptyCallback(self); + } + [self flushWaitingPool]; + }); +} + +- (void)flushWaitingPool +{ + asyncAtQueue(self.dataQueue, ^{ + [self _flushWaitingPool]; + }); +} +- (void)_flushWaitingPool +{ + if (self.runingPool.count < self.concurrent) + { + NSURLSessionTask *task = [self.waitingPool firstObject]; + if (task) + { + [self.runingPool addObject:task]; + [self.waitingPool removeObjectAtIndex:0]; + [task resume]; + [self _flushWaitingPool]; + } + } +} +@end @interface HNQueueManager() //queue index -@property (nonatomic) NSMutableDictionary* queueDict; +@property (nonatomic) NSMutableDictionary *queueDict; +@property (nonatomic) NSMutableDictionary *queueCallbackDict; @property (nonatomic) dispatch_queue_t myQueue; @end @@ -21,16 +115,17 @@ - (instancetype)init self = [super init]; if(self) { - _globalQueue = [[NSOperationQueue alloc] init]; - _queueDict = [[NSMutableDictionary alloc] init]; - _myQueue = dispatch_queue_create("HNQueueManager.queue", DISPATCH_QUEUE_SERIAL); + _globalQueue = [[HNQueue alloc] initWithConcurrent:10 name:@"global"]; + _queueDict = [NSMutableDictionary new]; + _queueCallbackDict = [NSMutableDictionary new]; + _myQueue = hCreateQueue("HNQueueManager.queue", DISPATCH_QUEUE_SERIAL); } return self; } #pragma mark - public methods -+ (id)instance ++ (instancetype)instance { static dispatch_once_t onceToken; static HNQueueManager* requestManager = nil; @@ -40,44 +135,72 @@ + (id)instance return requestManager; } -- (void)destoryOperationQueueWithName:(NSString *)name ++ (void)destoryOperationQueueWithName:(NSString *)name { - dispatch_sync(self.myQueue, ^{ - NSOperationQueue* operationQueue = [self.queueDict valueForKey:name]; + syncAtQueue([HNQueueManager instance].myQueue, ^{ + HNQueue* operationQueue = [[HNQueueManager instance].queueDict valueForKey:name]; if(operationQueue) { - [operationQueue cancelAllOperations]; - [self.queueDict setValue:nil forKey:name]; + [operationQueue cancelAllTask]; + [[HNQueueManager instance].queueDict setValue:nil forKey:name]; } }); } + (void)initQueueWithName:(NSString *)queueName maxMaxConcurrent:(NSInteger)maxMaxConcurrent { - dispatch_sync([HNQueueManager instance].myQueue, ^{ + syncAtQueue([HNQueueManager instance].myQueue, ^{ [[self instance] getOperationQueueWithName:queueName maxMaxConcurrent:maxMaxConcurrent]; }); } - ++ (void)queue:(NSString *)queueName finish:(void(^)(id sender))finish +{ + syncAtQueue([HNQueueManager instance].myQueue, ^{ + [[self instance] queue:queueName finish:finish]; + }); +} //get a queue by queue name -- (NSOperationQueue*)getOperationQueueWithName:(NSString*)name maxMaxConcurrent:(NSInteger)maxMaxConcurrent +- (HNQueue*)getOperationQueueWithName:(NSString*)name maxMaxConcurrent:(NSInteger)maxMaxConcurrent { - NSOperationQueue* operationQueue = [self.queueDict valueForKey:name]; + HNQueue* operationQueue = [self.queueDict valueForKey:name]; if(operationQueue == nil) { - operationQueue = [[NSOperationQueue alloc] init]; - [operationQueue setMaxConcurrentOperationCount:maxMaxConcurrent]; + operationQueue = [[HNQueue alloc] initWithConcurrent:maxMaxConcurrent name:name]; + @weakify(self) + [operationQueue setEmptyCallback:^(HNQueue *sender){ + @strongify(self) + asyncAtQueue(self.myQueue, ^{ + NSArray *callbacks = self.queueCallbackDict[sender.name]; + for (simple_callback aCallback in callbacks) + { + aCallback(sender.name); + } + [self.queueCallbackDict removeObjectForKey:sender.name]; + }); + }]; [self.queueDict setValue:operationQueue forKey:name]; } return operationQueue; } -- (NSOperationQueue*)getOperationQueueWithName:(NSString*)name +- (HNQueue*)getOperationQueueWithName:(NSString*)name { - __block NSOperationQueue* operationQueue; - dispatch_sync(self.myQueue, ^{ + __block HNQueue* operationQueue; + + syncAtQueue(self.myQueue, ^{ operationQueue = [self getOperationQueueWithName:name maxMaxConcurrent:1]; }); return operationQueue; } +- (void)queue:(NSString *)name finish:(simple_callback)finish +{ + if (name.length == 0 || finish == nil) return; + NSMutableArray *callbacks = self.queueCallbackDict[name]; + if (!callbacks) + { + callbacks = [NSMutableArray new]; + self.queueCallbackDict[name] = callbacks; + } + [callbacks addObject:finish]; +} @end diff --git a/Classes/Network/HNetworkDAO.h b/Classes/Network/HNetworkDAO.h index 9a861a4..b7af3a5 100644 --- a/Classes/Network/HNetworkDAO.h +++ b/Classes/Network/HNetworkDAO.h @@ -19,16 +19,19 @@ //in most situation, you use json Deserializer #import "HNJsonDeserializer.h" +#define HNDRedirectedResp @"HNDRedirectedResp" @class HNetworkDAO; -typedef void(^HNetworkDAOFinishBlock)(HNetworkDAO* request, id resultInfo); +typedef void(^HNetworkDAOSuccessBlock)(HNetworkDAO* request, id resultInfo); +typedef void(^HNetworkDAOFailBlock)(HNetworkDAO* request, NSError *error); +typedef void(^HNetworkDAOFinishBlock)(HNetworkDAO* request, id resultInfo, NSError *error); //ppx annotation #define HPHeader @"header" //if this property is a head attr in request, tag this //HPMapto: already defined in HDeserializableObject //HPIgnore: already defined in HDeserializableObject - +#define HNContentTypeApplicaitonJson @"application/json" /** * network data access operation @@ -37,7 +40,7 @@ typedef void(^HNetworkDAOFinishBlock)(HNetworkDAO* request, id resultInfo); * output: set 'deserializeKeyPath' to let it know witch part of data you concern, then set a kind of 'HEDeserialize' to 'deserializer' property. * the 'HEDeserialize' has tree imp, 'HNEntityDeserializer','HNArrayDescerializer','HNManualDescerializer', use any one kind for special data format * cache: read the cache type define, it is very simple and useful, and actrually most case could be constructed by cache instead of database. - * you need not concern about the cache size, it do every thing automaticly. + * you need not concern about the cache size, it do every thing automaticly. * use 'cache type' with some special 'cacheDuration' will get more flexable */ @interface HNetworkDAO : NSObject @@ -55,6 +58,8 @@ typedef void(^HNetworkDAOFinishBlock)(HNetworkDAO* request, id resultInfo); @property (nonatomic, strong) NSString *deserializeKeyPath; //timeout @property (nonatomic, assign) NSTimeInterval timeoutInterval; +//request content type , like application/json +@property (nonatomic) NSString *requstContentType; //upload progress or download progress @property (nonatomic, strong) HNProgressBlock progressBlock; //should continue when app is not active @@ -65,20 +70,24 @@ typedef void(^HNetworkDAOFinishBlock)(HNetworkDAO* request, id resultInfo); @property (nonatomic, strong) id userInfo; //rude data, if the request is file download ,it will not work @property (nonatomic, strong) NSData *responseData; +//http resp ,if it's a http response +@property (nonatomic, strong) NSHTTPURLResponse *httpResponse; //cache type @property (nonatomic) id cacheType; //if it is file download request, set the to YES. @property (nonatomic) BOOL isFileDownload; - +//is it on requesting +@property (nonatomic) BOOL onRequesting; //only use for subclass -@property (nonatomic, strong) HNetworkDAOFinishBlock sucessBlock; -@property (nonatomic, strong) HNetworkDAOFinishBlock failedBlock; - - +@property (nonatomic, strong) HNetworkDAOSuccessBlock sucessBlock; +@property (nonatomic, strong) HNetworkDAOFailBlock failedBlock; +@property (nonatomic, strong) HNetworkDAOFinishBlock finishBlock; //mock request @property (nonatomic) BOOL isMock; //default bundle is HNetworkDAO.bundle -@property (nonatomic, strong) NSString* mockBundlePath; +@property (nonatomic, strong) NSString* mockBundleName; +//default mock file name is ClassName.json +@property (nonatomic, strong) NSString* mockFileName; /** * begin request @@ -90,7 +99,7 @@ typedef void(^HNetworkDAOFinishBlock)(HNetworkDAO* request, id resultInfo); /** * begin request * - * @param queueName queue name, if you don't care about serail/concurrent set 'nil', + * @param queueName queue name, if you don't care about serail/concurrent set 'nil', * if you set a name ,it will create a serail queue automaticly if queue not exsit. * @param sucess callback when request success * @param failure callback when request fail @@ -120,11 +129,19 @@ typedef void(^HNetworkDAOFinishBlock)(HNetworkDAO* request, id resultInfo); * manual create a queue * if you want create a concurrent queue with special concurrent count, you should use this * - * @param queueName - * @param maxMaxConcurrent + * @param queueName queueName + * @param maxMaxConcurrent maxMaxConcurrent */ + (void)initQueueWithName:(NSString *)queueName maxMaxConcurrent:(NSInteger)maxMaxConcurrent; +/** + * + * set queue finish callback + * @param queueName queueName + * @param finish callback when queue finish + */ ++ (void)queue:(NSString *)queueName finish:(void(^)(id sender))finish; + /** * cancel current request, and cancel the operation */ @@ -132,18 +149,18 @@ typedef void(^HNetworkDAOFinishBlock)(HNetworkDAO* request, id resultInfo); /** * kill a operationQueue - * @param queueName + * @param queueName queueName * * @return is success */ + (BOOL)cancelQueueWithName:(NSString*)queueName; -#pragma mark - extention +#pragma mark - life circle /** * set request headers, default operation is search property of 'HPHeader' tag, then set the key and value - * @param headers: empty NSMutableDictionary + * @param headers empty NSMutableDictionary */ - (void)setupHeader:(NSMutableDictionary *)headers; @@ -167,20 +184,25 @@ typedef void(^HNetworkDAOFinishBlock)(HNetworkDAO* request, id resultInfo); /** * after set all params * usually we gen signature there - * @param params: contain all params + * @param params contain all params */ - (void)didSetupParams:(NSMutableDictionary *)params; +/** + * will send request + */ +- (void)willSendRequest:(NSString *)urlString method:(NSString *)method headers:(NSMutableDictionary *)headers params:(NSMutableDictionary *)params; + /** * will Send Request * you can do some log, encript progress there - * @param request + * @param request requst */ - (void)willSendRequest:(NSMutableURLRequest *)request; /** * send request * - * @param queueName + * @param queueName queueName */ - (void)startWithQueueName:(NSString*)queueName; @@ -188,11 +210,11 @@ typedef void(^HNetworkDAOFinishBlock)(HNetworkDAO* request, id resultInfo); * after recv response, default operation is invoke getOutputEntiy and write cache * u can do some status code examlation there */ -- (void)requestFinishedSucessWithInfo:(NSData *)responInfo response:(NSHTTPURLResponse *)response; +- (void)requestFinishedSucessWithInfo:(NSData *)responInfo response:(NSURLResponse *)response; /** * deal response data and convert to a object as new response, if return NSError, it will route to fail callback - * @param responseObject: rude response data, usally is a NSDictionary or NSArray by json decode + * @param responseObject rude response data, usally is a NSDictionary or NSArray by json decode * * @return object */ @@ -200,18 +222,27 @@ typedef void(^HNetworkDAOFinishBlock)(HNetworkDAO* request, id resultInfo); /** * after recv error, default operation is fail callback - * @param error + * @param error error */ - (void)requestFinishedFailureWithError:(NSError*)error; + + + +#pragma mark - other /** - * my cache key, + * my cache key, * u can special a request' cache key by rewrite this method. * return baseURL+pathURL by default * @return key */ - (NSString *)cacheKey; +/** + * get response string + * @return responseString + */ +- (NSString *)responseString; #ifdef DEBUG /** * custom Request,support local file.json @@ -219,8 +250,6 @@ typedef void(^HNetworkDAOFinishBlock)(HNetworkDAO* request, id resultInfo); - (void)doMockFileRequest; #endif -#pragma mark - other - //hold self /** @@ -247,4 +276,4 @@ typedef void(^HNetworkDAOFinishBlock)(HNetworkDAO* request, id resultInfo); @property (nonatomic) NSString *MIMEType; @property (nonatomic) long long length; @property (nonatomic) NSString *suggestedFilename; -@end \ No newline at end of file +@end diff --git a/Classes/Network/HNetworkDAO.m b/Classes/Network/HNetworkDAO.m index ab16bba..27ded56 100644 --- a/Classes/Network/HNetworkDAO.m +++ b/Classes/Network/HNetworkDAO.m @@ -113,9 +113,9 @@ - (void)startWithQueueName:(NSString*)queueName _queueName = queueName; - + NSString* urlString = [self fullurl]; - + #ifdef DEBUG if (self.isMock) { @@ -123,16 +123,20 @@ - (void)startWithQueueName:(NSString*)queueName return; } #endif - - + + NSURL *testURL = [NSURL URLWithString:urlString]; + if (!testURL || !testURL.host) { + [self requestFinishedFailureWithError:herr(kNoDataErrorCode, ([NSString stringWithFormat:@"url is not approved %@", urlString]))]; + return; + } //prepare file download path if (self.isFileDownload) { self.fileDownloadPath = [self createTempFilePath:urlString]; } else self.fileDownloadPath = nil; - - + + //if is file access url if ([urlString hasPrefix:@"file://"]) { @@ -161,76 +165,92 @@ - (void)startWithQueueName:(NSString*)queueName id params = [self setupParams]; //request - __weak HNetworkDAO* weakSelf = self; + @weakify(self) _holdSelf = self; - dispatch_async(dispatch_get_global_queue(0, 0), ^{ - - NSString *className = [HClassManager getClassNameForKey:HNetworkProviderRegKey]; - Class class = NSClassFromString(className); - - if (!class) - { - [self requestFinishedFailureWithError:herr(kInnerErrorCode, @"can't find any Class for HNetworkProviderRegKey")]; - return; - } + + NSString *className = [HClassManager getClassNameForKey:HNetworkProviderRegKey]; + Class class = NSClassFromString(className); + + if (!class) + { + [self requestFinishedFailureWithError:herr(kInnerErrorCode, @"can't find any Class for HNetworkProviderRegKey")]; + return; + } + if (self.provider) + { + [self.provider cancel]; + self.provider = nil; + } + self.provider = [class new]; + if (![self.provider conformsToProtocol:@protocol(HNetworkProvider)]) + { + [self requestFinishedFailureWithError:herr(kInnerErrorCode, ([NSString stringWithFormat:@"%@ is not a HNetworkProvider", className]))]; + return; + } + + [self willSendRequest:urlString method:self.method headers:headers params:params]; + + [self.provider setUrlString:urlString]; + [self.provider setHeadParameters:headers]; + [self.provider setParams:params]; + [self.provider setMethod:self.method]; + [self.provider setRequstContentType:self.requstContentType]; + NSLog(@"mehtod = %@", [self.provider method]); +// if([self.provider method] == nil) +// { +// NSAssert(NO, @""); +// } + [self.provider setQueueName:queueName]; + + [self.provider setTimeoutInterval:self.timeoutInterval]; + [self.provider setShouldContinueInBack:self.shouldContinueInBack]; + [self.provider setFileDownloadPath:self.fileDownloadPath]; + if ([self.cacheType isKindOfClass:[HNSystemCacheStrategy class]]) + { + [self.provider setCachePolicy:[(HNSystemCacheStrategy *)self.cacheType policy]]; + } + [self.provider setSuccessCallback:^(id sender, NSURLResponse *response, NSData *data){ + @strongify(self) + NSLog(@"#### revc response:\n%@", [self fullurl]); - self.provider = [class new]; - if (![self.provider conformsToProtocol:@protocol(HNetworkProvider)]) + if (!self.fileDownloadPath) { - [self requestFinishedFailureWithError:herr(kInnerErrorCode, ([NSString stringWithFormat:@"%@ is not a HNetworkProvider", className]))]; - return; + self.responseData = data; + if ([response isKindOfClass:[NSHTTPURLResponse class]]) self.httpResponse = (NSHTTPURLResponse *)response; + [self requestFinishedSucessWithInfo:data response:response]; } - - [self.provider setUrlString:urlString]; - [self.provider setParams:params]; - [self.provider setMethod:self.method]; - [self.provider setQueueName:queueName]; - - [self.provider setTimeoutInterval:self.timeoutInterval]; - [self.provider setShouldContinueInBack:self.shouldContinueInBack]; - [self.provider setFileDownloadPath:self.fileDownloadPath]; - [self.provider setHeadParameters:headers]; - if ([self.cacheType isKindOfClass:[HNSystemCacheStrategy class]]) + else { - [self.provider setCachePolicy:[(HNSystemCacheStrategy *)self.cacheType policy]]; + HDownloadFileInfo *info = [HDownloadFileInfo new]; + info.filePath = self.fileDownloadPath; + info.MIMEType = [response MIMEType]; + info.length = [response expectedContentLength]; + info.suggestedFilename = [response suggestedFilename]; + //delete after 1 min + [[HFileCache shareCache] setExpire:[NSDate dateWithTimeIntervalSinceNow:3600] forFilePath:info.filePath]; + [self downloadFinished:info]; } - [self.provider setSuccessCallback:^(id sender, NSHTTPURLResponse *response, NSData *data){ - - NSLog(@" "); - NSLog(@"#### revc response"); - NSLog(@"#### %@", [weakSelf fullurl]); - NSLog(@" "); - - if (!weakSelf.fileDownloadPath) - { - weakSelf.responseData = data; - [weakSelf requestFinishedSucessWithInfo:data response:response]; - } - else - { - HDownloadFileInfo *info = [HDownloadFileInfo new]; - info.filePath = weakSelf.fileDownloadPath; - info.MIMEType = [response MIMEType]; - info.length = [response expectedContentLength]; - info.suggestedFilename = [response suggestedFilename]; - //delete after 1 min - [[HFileCache shareCache] setExpire:[NSDate dateWithTimeIntervalSinceNow:60] forFilePath:info.filePath]; - [weakSelf downloadFinished:info]; - } - }]; - - [self.provider setFailCallback:^(id sender, NSError *error){ - [weakSelf requestFinishedFailureWithError:[NSError errorWithDomain:@"Network" code:error.code description:error.localizedDescription]]; - }]; - - [self.provider setProgressCallback:self.progressBlock]; - - [self.provider setWillSendCallback:^(NSMutableURLRequest *request){ - [weakSelf willSendRequest:request]; - }]; - - [self.provider sendRequest]; - }); + }]; + + [self.provider setFailCallback:^(id sender, NSURLResponse *response, NSError *error){ + @strongify(self) + if ([response isKindOfClass:[NSHTTPURLResponse class]]) self.httpResponse = (NSHTTPURLResponse *)response; + [self requestFinishedFailureWithError:[NSError errorWithDomain:@"Network" code:error.code description:error.localizedDescription]]; + }]; + + [self.provider setProgressCallback:^(id sender, double progress){ + @strongify(self) + if (self.progressBlock) self.progressBlock(self, progress); + }]; + + [self.provider setWillSendCallback:^(NSMutableURLRequest *request){ + @strongify(self) + [self willSendRequest:request]; + }]; + + + [self.provider sendRequest]; + } } - (void)start:(void(^)(id sender, id data))sucess failure:(void(^)(id sender, NSError *error))failure @@ -241,10 +261,15 @@ - (void)startWithQueueName:(NSString *)queueName sucess:(void (^)(id, id))sucess failure:(void (^)(id, NSError *))failure { + self.onRequesting = YES; _sucessBlock = sucess; _failedBlock = failure; - [self cacheLogic:queueName]; - + @weakify(self) + [self cacheLogic:0 error:nil continueCallback:^(id res){ + @strongify(self) + [self startWithQueueName:queueName]; + }]; + } - (void)start:(void(^)(id sender, id data, NSError *error))finish { @@ -253,18 +278,25 @@ - (void)start:(void(^)(id sender, id data, NSError *error))finish - (void)startWithQueueName:(NSString *)queueName finish:(void(^)(id sender, id data, NSError *error))finish { - _sucessBlock = ^(id sender, id data){ - if (finish) finish(sender, data, nil); - }; - _failedBlock = ^(id sender, NSError *error){ - if (finish) finish(sender, nil, error); - }; - [self cacheLogic:queueName]; + _finishBlock = finish; + @weakify(self) + [self cacheLogic:0 error:nil continueCallback:^(id res){ + @strongify(self) + [self startWithQueueName:queueName]; + }]; +} +- (void)_clear { + //clear + _failedBlock = nil; + _sucessBlock = nil; + _finishBlock = nil; + _holdSelf = nil; + self.onRequesting = NO; } - - (void)cancel { [self.provider cancel]; + [self _clear]; } - (void)setupHeader:(NSMutableDictionary *)headers @@ -292,7 +324,7 @@ - (void)setupParams:(NSMutableDictionary *)params { if (self.class == [HNetworkDAO class]) return; NSArray* pplist = [self ppList]; - + for(NSString* key in pplist) { NSArray *exts = [[self class] annotations:key]; @@ -313,22 +345,22 @@ - (void)setupParams:(NSMutableDictionary *)params } - (void)didSetupParams:(NSMutableDictionary *)params { - + +} +- (void)willSendRequest:(NSString *)urlString method:(NSString *)method headers:(NSMutableDictionary *)headers params:(NSMutableDictionary *)params +{ } - (id)processData:(NSData *)responseInfo { [self.deserializer setDeserializeKeyPath:self.deserializeKeyPath]; - + id processRes = responseInfo; if ([self.deserializer respondsToSelector:@selector(preprocess:)]) { processRes = [self.deserializer preprocess:responseInfo]; if ([processRes isKindOfClass:[NSError class]]) { - assert(NO); - NSLog(@"parse data error %@", [processRes description]); - [self requestFinishedFailureWithError:processRes]; - return nil; + return processRes; } } @@ -336,13 +368,7 @@ - (id)processData:(NSData *)responseInfo if (!responseEntity) { NSString *errorStr = [NSString stringWithFormat:@"inner error:%@.getOutputEntiy return nil", NSStringFromClass(self.class)]; - [self requestFinishedFailureWithError:herr(kInnerErrorCode, errorStr)]; - return nil; - } - if ([responseEntity isKindOfClass:[NSError class]]) - { - [self requestFinishedFailureWithError:responseEntity]; - return nil; + return herr(kInnerErrorCode, errorStr); } return responseEntity; } @@ -356,7 +382,7 @@ - (id)getOutputEntiy:(id)responseObject //local request - (void)doLocalFileRequest:(NSString *)urlString { - dispatch_async(dispatch_get_global_queue(0, 0), ^{ + asyncAtQueue(dispatch_get_global_queue(0, 0), ^{ NSData *fileData = [NSData dataWithContentsOfURL:[NSURL URLWithString:urlString]]; if (!fileData) { @@ -365,7 +391,7 @@ - (void)doLocalFileRequest:(NSString *)urlString } if (!self.isFileDownload) { - NSLog(@"revc response %@/%@", self.baseURL, self.pathURL); + NSLog(@"\n\n#### revc response \n%@", [self fullurl]); self.responseData = fileData; [self requestFinishedSucessWithInfo:fileData response:nil]; } @@ -390,33 +416,35 @@ - (void)doLocalFileRequest:(NSString *)urlString //mock request - (void)doMockFileRequest { - NSString *urlString = @"HNetworkDAO.bundle"; - NSBundle *mockFileBundle = [NSBundle bundleWithPath:[[NSBundle mainBundle] pathForResource:@"HNetworkDAO" ofType:@"bundle"]]; - - if (self.mockBundlePath) + NSString *bundleName = @"HNetworkDAO.bundle"; + if (self.mockBundleName) { - mockFileBundle = [NSBundle bundleWithURL:[NSURL URLWithString:self.mockBundlePath]]; + bundleName = self.mockBundleName; } - + NSBundle *mockFileBundle = [NSBundle bundleWithPath:[[NSBundle mainBundle] pathForResource:bundleName ofType:nil]]; + if (mockFileBundle) { - NSString *fileType = nil; - if ([self.deserializer respondsToSelector:@selector(mockFileType)]) fileType = [self.deserializer mockFileType]; - - urlString = [mockFileBundle pathForResource:NSStringFromClass([self class]) ofType:fileType]; - if (urlString) + NSString *mockFilePath = nil; + if (self.mockFileName) { - urlString = [NSURL fileURLWithPath:urlString].absoluteString; - [self doLocalFileRequest:urlString]; - return; + mockFilePath = [mockFileBundle pathForResource:self.mockFileName ofType:nil]; } else { - urlString = [NSString stringWithFormat:@"%@.%@",NSStringFromClass([self class]),fileType]; + NSString *fileType = nil; + if ([self.deserializer respondsToSelector:@selector(mockFileType)]) fileType = [self.deserializer mockFileType]; + mockFilePath = [mockFileBundle pathForResource:NSStringFromClass([self class]) ofType:fileType]; } + + mockFilePath = [NSURL fileURLWithPath:mockFilePath].absoluteString; + [self doLocalFileRequest:mockFilePath]; + } + else + { + [self requestFinishedFailureWithError:[NSError errorWithDomain:@"Network" code:kInnerErrorCode description:[NSString stringWithFormat:@"%@ not exsit", bundleName]]]; } - [self requestFinishedFailureWithError:[NSError errorWithDomain:@"Network" code:kInnerErrorCode description:[NSString stringWithFormat:@"%@ file not exsit", urlString]]]; } #endif @@ -427,145 +455,134 @@ - (NSString *)cacheKey return [NSString stringWithFormat:@"%@%@",self.baseURL, self.pathURL]; } -- (void)cacheLogic:(NSString *)queueName + +/// 缓存处理逻辑 +/// - Parameters: +/// - stage: 阶段 0:请求前, 1:失败后 +/// - error: 失败时的错误,1的时候才填 +/// - continueCallback: 是否继续原逻辑过程 +- (void)cacheLogic:(int)stage error:(NSError *)error continueCallback:(simple_callback)continueCallback { if ([self.cacheType isKindOfClass:[HNCustomCacheStrategy class]]) { HNCustomCacheStrategy *customCacheStrategy = self.cacheType; customCacheStrategy.cacheKey = self.cacheKey; - __weak typeof(self) weakSelf = self; - [customCacheStrategy cacheLogic:^(BOOL shouldRequest, NSData *cachedData) { + @weakify(self) + @weakify(customCacheStrategy); + + HNCustomCacheCallback cachaLogCallback = ^(BOOL shouldContinue, NSData *cachedData) { + @strongify(self) + @strongify(customCacheStrategy); + id res = nil; if (cachedData) { - id responseEntity = [weakSelf processData:cachedData]; - if (!responseEntity) return; //has deal all exception - else if(weakSelf.sucessBlock) weakSelf.sucessBlock(nil, responseEntity); - } - if (shouldRequest) - { - [weakSelf startWithQueueName:queueName]; + res = [self processData:cachedData]; + if ([res isKindOfClass:[NSError class]] || [res isEqual:HNDRedirectedResp]) + { + NSLog(@"warning : cache data is not correct, it will be deleted"); + //delete cache + [customCacheStrategy deleteCache]; + shouldContinue = YES; + } + else + { + asyncAtMain(^{ + [self finishCallback:nil res:res error:error]; + [self _clear]; + }); + } } - else + if (shouldContinue) { - weakSelf.failedBlock = nil; - weakSelf.sucessBlock = nil; - weakSelf.holdSelf = nil; + continueCallback(res); } - }]; + }; + if (stage == 0) { + [customCacheStrategy cacheLogic:cachaLogCallback]; + } + else if (stage == 1) { + [customCacheStrategy cacheLogicOnFail:error cacheCallback:cachaLogCallback]; + } + else { + continueCallback(nil); + } + } + else { + continueCallback(nil); } - else [self startWithQueueName:queueName]; -// NSString *cacheKey = [self cacheKey]; -// switch (self.cacheType) { -// case HFileCacheTypeNone: -// { -// [self startWithQueueName:queueName]; -// break; -// } -// case HFileCacheTypeBoth: -// { -// [self loadCache:cacheKey]; -// [self startWithQueueName:queueName]; -// break; -// } -// case HFileCacheTypeExclusive: -// { -// if (![self isCacheUseable:cacheKey]) -// { -// [self startWithQueueName:queueName]; -// } -// else -// { -// [self loadCache:cacheKey]; -// //解除保持 -// _failedBlock = nil; -// _sucessBlock = nil; -// _holdSelf = nil; -// } -// break; -// } -// case HFileCacheTypeForceRefresh: -// { -// [self startWithQueueName:queueName]; -// break; -// } -// default: -// break; -// } } + #pragma mark - queue + (void)initQueueWithName:(NSString *)queueName maxMaxConcurrent:(NSInteger)maxMaxConcurrent { [HNQueueManager initQueueWithName:queueName maxMaxConcurrent:maxMaxConcurrent]; } - ++ (void)queue:(NSString *)queueName finish:(void(^)(id sender))finish +{ + [HNQueueManager queue:queueName finish:finish]; +} + (BOOL)cancelQueueWithName:(NSString*)queueName { if(queueName) { - [[HNQueueManager instance] destoryOperationQueueWithName:queueName]; + [HNQueueManager destoryOperationQueueWithName:queueName]; return YES; } return NO; } #pragma mark - netWorking finished - -- (void)requestFinishedSucessWithInfo:(NSData *)responInfo response:(NSHTTPURLResponse *)response +- (void)finishCallback:(id)sender res:(id)res error:(NSError *)error { + if (self->_finishBlock) self->_finishBlock(sender, res, error); + if (res && self->_sucessBlock) self->_sucessBlock(sender, res); + if (error && self->_failedBlock) self->_failedBlock(sender, error); +} +- (void)requestFinishedSucessWithInfo:(NSData *)responInfo response:(NSURLResponse *)response { - if ([self.cacheType isKindOfClass:[HNCustomCacheStrategy class]]) + id res = [self processData:responInfo]; + if ([res isKindOfClass:[NSError class]]) { - HNCustomCacheStrategy *customCacheStrategy = self.cacheType; - responInfo = [customCacheStrategy handleRespInfo:responInfo]; + [self requestFinishedFailureWithError:res]; } - id responseEntity = [self processData:responInfo]; - if (!responseEntity) + else if ([res isEqual:HNDRedirectedResp]) + { + return; + } + else { - //delete cache if ([self.cacheType isKindOfClass:[HNCustomCacheStrategy class]]) { HNCustomCacheStrategy *customCacheStrategy = self.cacheType; - [customCacheStrategy deleteCache]; + [customCacheStrategy handleRespInfo:responInfo]; } - return; //has deal all exception + asyncAtMain(^{ + [self finishCallback:self res:res error:nil]; + [self _clear]; + }); } - - - dispatch_async(dispatch_get_main_queue(), ^{ - if(_sucessBlock) - _sucessBlock(self, responseEntity); - - //clear - _failedBlock = nil; - _sucessBlock = nil; - _holdSelf = nil; - }); } - (void)requestFinishedFailureWithError:(NSError*)error { - NSLog(@"error:%li,%@,%@ url = %@/%@", (long)error.code,error.domain,error.localizedDescription, self.baseURL, self.pathURL); - dispatch_async(dispatch_get_main_queue(), ^{ - if(_failedBlock) - _failedBlock(self, error); - - //clear - _failedBlock = nil; - _sucessBlock = nil; - _holdSelf = nil; - }); + NSLog(@"\n\n#### request error:\n%li,%@,%@ \n url = %@", (long)error.code,error.domain,error.localizedDescription, [self fullurl]); + if (self.responseData) NSLog(@"data:\n%@", [[NSString alloc] initWithData:self.responseData encoding:NSUTF8StringEncoding]); + @weakify(self) + [self cacheLogic:1 error:error continueCallback:^(id res){ + asyncAtMain(^{ + @strongify(self) + [self finishCallback:self res:res error:error]; + [self _clear]; + }); + }]; + } - (void)downloadFinished:(HDownloadFileInfo *)info { - dispatch_async(dispatch_get_main_queue(), ^{ - if(_sucessBlock) - _sucessBlock(self, info); - - //clear - _failedBlock = nil; - _sucessBlock = nil; - _holdSelf = nil; + asyncAtMain(^{ + [self finishCallback:self res:info error:nil]; + [self _clear]; }); } @@ -588,6 +605,10 @@ - (void)unHoldNetwork { _holdSelf = nil; } +- (NSString *)responseString +{ + return [[NSString alloc] initWithData:self.responseData encoding:NSUTF8StringEncoding]; +} @end diff --git a/Classes/Network/HNetworkProvider.h b/Classes/Network/HNetworkProvider.h index f0e9d7e..b1200b6 100644 --- a/Classes/Network/HNetworkProvider.h +++ b/Classes/Network/HNetworkProvider.h @@ -8,9 +8,9 @@ #import -typedef void (^HNPSuccessCallback)(id sender, NSHTTPURLResponse *reponse, NSData *data); -typedef void (^HNPFailCallback)(id sender, NSError *error); -typedef void (^HNProgressBlock)(NSUInteger bytesWritten, long long totalBytesWritten, long long totalBytesExpectedToWrite); +typedef void (^HNPSuccessCallback)(id sender, NSURLResponse *response, NSData *data); +typedef void (^HNPFailCallback)(id sender, NSURLResponse *response, NSError *error); +typedef void (^HNProgressBlock)(id sender, double progress); typedef void (^HNPWillSendCallback)(NSMutableURLRequest *request); @@ -21,7 +21,7 @@ typedef void (^HNPWillSendCallback)(NSMutableURLRequest *request); @property (nonatomic) id params; @property (nonatomic) NSString *method; @property (nonatomic) NSString *queueName; - +@property (nonatomic) NSString *requstContentType; @property (nonatomic) NSTimeInterval timeoutInterval; @property (nonatomic) BOOL shouldContinueInBack; @property (nonatomic) NSString *fileDownloadPath; diff --git a/Demo/DeserializeDemo.m b/Demo/DeserializeDemo.m index 563ff97..886200e 100644 --- a/Demo/DeserializeDemo.m +++ b/Demo/DeserializeDemo.m @@ -29,7 +29,7 @@ - (instancetype)init { NSLog(@"%@",entity.format_error); } - else NSLog(@"deserialize success:%@", [entity jsonString]); + else NSLog(@"deserialize success:%@", [entity h_jsonString]); }]; [self addMenu:@"deserialize array" callback:^(id sender, id data) { @@ -47,7 +47,7 @@ - (instancetype)init { NSLog(@"%@",entity.format_error); } - else NSLog(@"deserialize success:%@", [entity jsonString]); + else NSLog(@"deserialize success:%@", [entity h_jsonString]); }]; [self addMenu:@"deserialize array 2,innertype tag" callback:^(id sender, id data) { NSDictionary *dict = @{@"c":@(1), @@ -73,7 +73,7 @@ - (instancetype)init { NSLog(@"%@",entity.format_error); } - else NSLog(@"deserialize success:%@", [entity jsonString]); + else NSLog(@"deserialize success:%@", [entity h_jsonString]); }]; [self addMenu:@"check empty, nil" callback:^(id sender, id data) { NSDictionary *dict = @{@"a":@(1),@"b":@(2),@"c":@"c"}; @@ -83,7 +83,7 @@ - (instancetype)init { NSLog(@"%@",entity.format_error); } - else NSLog(@"deserialize success:%@", [entity jsonString]); + else NSLog(@"deserialize success:%@", [entity h_jsonString]); }]; [self addMenu:@"check empty, nil 2" callback:^(id sender, id data) { @@ -94,7 +94,7 @@ - (instancetype)init { NSLog(@"%@",entity.format_error); } - else NSLog(@"deserialize success:%@", [entity jsonString]); + else NSLog(@"deserialize success:%@", [entity h_jsonString]); }]; [self addMenu:@"HPOptional tag" callback:^(id sender, id data) { @@ -105,7 +105,7 @@ - (instancetype)init { NSLog(@"%@",entity.format_error); } - else NSLog(@"deserialize success:%@", [entity jsonString]); + else NSLog(@"deserialize success:%@", [entity h_jsonString]); }]; [self addMenu:@"check type" callback:^(id sender, id data) { @@ -116,7 +116,7 @@ - (instancetype)init { NSLog(@"%@",entity.format_error); } - else NSLog(@"deserialize success:%@", [entity jsonString]); + else NSLog(@"deserialize success:%@", [entity h_jsonString]); }]; [self addMenu:@"check type 2" callback:^(id sender, id data) { @@ -127,7 +127,7 @@ - (instancetype)init { NSLog(@"%@",entity.format_error); } - else NSLog(@"deserialize success:%@", [entity jsonString]); + else NSLog(@"deserialize success:%@", [entity h_jsonString]); }]; [self addMenu:@"check type 3" callback:^(id sender, id data) { @@ -138,7 +138,7 @@ - (instancetype)init { NSLog(@"%@",entity.format_error); } - else NSLog(@"deserialize success:%@", [entity jsonString]); + else NSLog(@"deserialize success:%@", [entity h_jsonString]); }]; [self addMenu:@"check type 4" callback:^(id sender, id data) { @@ -149,7 +149,7 @@ - (instancetype)init { NSLog(@"%@",entity.format_error); } - else NSLog(@"deserialize success:%@", [entity jsonString]); + else NSLog(@"deserialize success:%@", [entity h_jsonString]); }]; [self addMenu:@"check value scope 1" callback:^(id sender, id data) { NSDictionary *dict = @{@"a":@(11),@"b":@"12",@"c":@{ @@ -163,7 +163,7 @@ - (instancetype)init { NSLog(@"%@",entity.format_error); } - else NSLog(@"deserialize success:%@", [entity jsonString]); + else NSLog(@"deserialize success:%@", [entity h_jsonString]); }]; [self addMenu:@"check value scope 2" callback:^(id sender, id data) { NSDictionary *dict = @{@"a":@"12",@"b":@"12",@"c":@{ @@ -177,7 +177,7 @@ - (instancetype)init { NSLog(@"%@",entity.format_error); } - else NSLog(@"deserialize success:%@", [entity jsonString]); + else NSLog(@"deserialize success:%@", [entity h_jsonString]); }]; [self addMenu:@"a complex deserializing" callback:^(id sender, id data) { @@ -198,7 +198,7 @@ - (instancetype)init { NSLog(@"%@",entity.format_error); } - else NSLog(@"deserialize success:%@", [entity jsonString]); + else NSLog(@"deserialize success:%@", [entity h_jsonString]); }]; [self addMenu:@"property annotation" callback:^(id sender, id data) { @@ -209,7 +209,7 @@ - (instancetype)init { NSLog(@"%@",entity.format_error); } - else NSLog(@"deserialize success:%@", [entity jsonString]); + else NSLog(@"deserialize success:%@", [entity h_jsonString]); }]; [self addMenu:@"autocast test" callback:^(id sender, id data) { @@ -220,7 +220,7 @@ - (instancetype)init { NSLog(@"%@",entity.format_error); } - else NSLog(@"deserialize success:%@", [entity jsonString]); + else NSLog(@"deserialize success:%@", [entity h_jsonString]); }]; [self addMenu:@"autocast test2" callback:^(id sender, id data) { @@ -231,7 +231,7 @@ - (instancetype)init { NSLog(@"%@",entity.format_error); } - else NSLog(@"deserialize success:%@ date = %@", [entity jsonString], [entity.d displayDesc]); + else NSLog(@"deserialize success:%@ date = %@", [entity h_jsonString], [entity.d displayDesc]); }]; [self addMenu:@"entity with protocal test" callback:^(id sender, id data) { @@ -242,7 +242,7 @@ - (instancetype)init { NSLog(@"%@",entity.format_error); } - else NSLog(@"deserialize success:%@", [entity jsonString]); + else NSLog(@"deserialize success:%@", [entity h_jsonString]); }]; [self addMenu:@"entity contain id test" callback:^(id sender, id data) { @@ -254,7 +254,7 @@ - (instancetype)init { NSLog(@"%@",entity.format_error); } - else NSLog(@"deserialize success:%@", [entity jsonString]); + else NSLog(@"deserialize success:%@", [entity h_jsonString]); }]; [self addMenu:@"HPDivideType test" callback:^(id sender, id data) { @@ -270,7 +270,7 @@ - (instancetype)init { NSLog(@"%@",entity.format_error); } - else NSLog(@"deserialize success:%@", [entity jsonString]); + else NSLog(@"deserialize success:%@", [entity h_jsonString]); }]; [self addMenu:@"Reserved works test" callback:^(id sender, id data) { @@ -281,7 +281,25 @@ - (instancetype)init { NSLog(@"%@",entity.format_error); } - else NSLog(@"deserialize success:%@", [entity jsonString]); + else NSLog(@"deserialize success:%@", [entity h_jsonString]); + }]; + + [self addMenu:@"NSCopying" callback:^(id sender, id data) { + NSDictionary *dict = @{@"a":@"a",@"b":@[ + @{@"c":@(1), + @"d":@{@"x":@(3),@"y":@(4.2),@"z":@(5.5)}, + @"e":@[ + @{@"a":@(1),@"b":@"1##",@"c":@{@"x":@(3),@"y":@(4.2),@"z":@(5.5)}}, + @{@"a":@(1),@"b":@"2##",@"c":@{@"x":@(3),@"y":@(4.2),@"z":@(5.5)}}, + @{@"a":@(1),@"b":@"3##",@"c":@{@"x":@(3),@"y":@(4.2),@"z":@(5.5)}} + ]} + ]}; + + TestEntity5 *entity = [TestEntity5 new]; + [entity setWithDictionary:dict]; + + TestEntity5 *copy = [entity copy]; + NSLog(@"%@", [copy h_jsonString]); }]; } return self; @@ -295,5 +313,6 @@ - (void)viewDidLoad [self.view addSubview:bg]; [super viewDidLoad]; self.tableView.backgroundColor = [UIColor clearColor]; + } @end diff --git a/Demo/MenuVC.m b/Demo/MenuVC.m index f834c10..0c6ef66 100644 --- a/Demo/MenuVC.m +++ b/Demo/MenuVC.m @@ -80,21 +80,21 @@ - (void)testDBDAO [userDao removes:nil]; NSLog(@"create new data model"); User *newUser = [self newUser]; - NSLog(@"I have create a user: \n%@",[newUser jsonString]); + NSLog(@"I have create a user: \n%@",[newUser h_jsonString]); [userDao add:newUser]; NSString *ID = [userDao lastInsertedID]; NSLog(@"last inserted ID is %@",ID); NSLog(@"query User by ID:%@",ID); User *auser = (User *)[userDao get:ID]; - NSLog(@"User from db:\n%@",[auser jsonString]); + NSLog(@"User from db:\n%@",[auser h_jsonString]); NSLog(@"update the user,change desc to ‘new desc’"); auser.desc = @"new desc"; [userDao update:auser]; NSLog(@"query the User after update"); User *updatedUser = (User *)[userDao get:ID]; - NSLog(@"%@",[updatedUser jsonString]); - NSLog(@"the 'desc' and 'modified' of the old object has been changed\n%@",[auser jsonString]); + NSLog(@"%@",[updatedUser h_jsonString]); + NSLog(@"the 'desc' and 'modified' of the old object has been changed\n%@",[auser h_jsonString]); NSLog(@"insert 10 item"); for (int i = 0; i < 10; i ++) { @@ -104,13 +104,13 @@ - (void)testDBDAO NSArray *users = [userDao list:nil]; for (User *auser in users) { - NSLog(@"%@", [auser jsonString]); + NSLog(@"%@", [auser h_jsonString]); } NSLog(@"condition query: sex=0"); users = [userDao list:@"sex = 0"]; for (User *auser in users) { - NSLog(@"%@", [auser jsonString]); + NSLog(@"%@", [auser h_jsonString]); } NSLog(@"query count"); long count = [userDao count:@"sex = 0"]; @@ -130,7 +130,7 @@ - (void)testDBDAO users = [userDao list:@"sex = 0"]; for (User *user in users) { - NSLog(@"%@",[user jsonString]); + NSLog(@"%@",[user h_jsonString]); } } @@ -142,21 +142,21 @@ - (void)testDBEntity [User removes:nil]; NSLog(@"create new data model"); User *newUser = [self newUser]; - NSLog(@"I have create a user: \n%@",[newUser jsonString]); + NSLog(@"I have create a user: \n%@",[newUser h_jsonString]); [newUser save]; NSString *ID = [User lastInsertedID]; NSLog(@"last inserted ID is %@",ID); NSLog(@"query User by ID:%@",ID); User *auser = (User *)[User get:ID]; - NSLog(@"User from db:\n%@",[auser jsonString]); + NSLog(@"User from db:\n%@",[auser h_jsonString]); NSLog(@"update the user,change desc to ‘new desc’"); auser.desc = @"new desc"; [auser update]; NSLog(@"query the User after update"); User *updatedUser = (User *)[User get:ID]; - NSLog(@"%@",[updatedUser jsonString]); - NSLog(@"the 'desc' and 'modified' of the old object has been changed\n%@",[auser jsonString]); + NSLog(@"%@",[updatedUser h_jsonString]); + NSLog(@"the 'desc' and 'modified' of the old object has been changed\n%@",[auser h_jsonString]); NSLog(@"insert 10 item"); for (int i = 0; i < 10; i ++) { @@ -166,13 +166,13 @@ - (void)testDBEntity NSArray *users = [User list:nil]; for (User *auser in users) { - NSLog(@"%@", [auser jsonString]); + NSLog(@"%@", [auser h_jsonString]); } NSLog(@"condition query: sex=0"); users = [User list:@"sex = 0"]; for (User *auser in users) { - NSLog(@"%@", [auser jsonString]); + NSLog(@"%@", [auser h_jsonString]); } NSLog(@"query count"); long count = [User count:@"sex = 0"]; @@ -192,7 +192,7 @@ - (void)testDBEntity users = [User list:@"sex = 0"]; for (User *user in users) { - NSLog(@"%@",[user jsonString]); + NSLog(@"%@",[user h_jsonString]); } } @@ -202,14 +202,14 @@ - (void)testDBEntity2 [Student removes:nil]; NSLog(@"create new Student model"); Student *newStudent = [self newStudent]; - NSLog(@"I have create a Student: \n%@",[newStudent jsonString]); + NSLog(@"I have create a Student: \n%@",[newStudent h_jsonString]); [newStudent save]; NSString *ID = [Student lastInsertedID]; NSLog(@"last inserted ID is %@",ID); NSLog(@"query Student by ID:%@",ID); Student *aStudent = (Student *)[Student get:ID]; - NSLog(@"Student from db:\n%@",[aStudent jsonString]); + NSLog(@"Student from db:\n%@",[aStudent h_jsonString]); NSLog(@"total Student count : %li", [Student count:nil]); } diff --git a/Demo/NetworkDaoTestVC.h b/Demo/NetworkDaoTestVC.h index d98e521..fc293d0 100644 --- a/Demo/NetworkDaoTestVC.h +++ b/Demo/NetworkDaoTestVC.h @@ -27,4 +27,18 @@ @interface DemoEntity : HEntity @property (nonatomic) NSString *text; @property (nonatomic) NSMutableArray *otherInfo; -@end \ No newline at end of file +@end + + +@interface DemoJsonDaoEntity : NSObject +@property (nonatomic) NSString *a; +@property (nonatomic) int b; +@property (nonatomic) NSArray *objs; +@end +DemoJsonDaoEntity *CdemoJsonDaoEntity(NSString *a, int b); +@interface DemoJsonDao : HNetworkDAO +@property (nonatomic) DemoJsonDaoEntity *obj; +@property (nonatomic) NSString *s; +@end + + diff --git a/Demo/NetworkDaoTestVC.m b/Demo/NetworkDaoTestVC.m index 9454672..af9fcd4 100644 --- a/Demo/NetworkDaoTestVC.m +++ b/Demo/NetworkDaoTestVC.m @@ -16,7 +16,6 @@ #import "TestNetworkDAO.h" #import "TestEntity1.h" -#import "PBNetworkDaoTestVC.h" @interface NetworkDaoTestVC () @property (nonatomic) UIView *textBack; @@ -44,7 +43,7 @@ - (instancetype)init NSString *orgStr = [[NSString alloc] initWithData:sender.responseData encoding:NSUTF8StringEncoding]; NSLog(@"error: %@\n orignal: %@", error, orgStr); } - else NSLog(@"resp: %@\n%@", NSStringFromClass([data class]), [data jsonString]); + else NSLog(@"resp: %@\n%@", NSStringFromClass([data class]), [data h_jsonString]); }]; @@ -60,7 +59,7 @@ - (instancetype)init NSString *orgStr = [[NSString alloc] initWithData:sender.responseData encoding:NSUTF8StringEncoding]; NSLog(@"error: %@\n orignal:%@", error, orgStr); } - else NSLog(@"resp: %@\n%@", NSStringFromClass([data class]), [data jsonString]); + else NSLog(@"resp: %@\n%@", NSStringFromClass([data class]), [data h_jsonString]); }]; }]; @@ -73,7 +72,7 @@ - (instancetype)init NSString *orgStr = [[NSString alloc] initWithData:sender.responseData encoding:NSUTF8StringEncoding]; NSLog(@"error: %@\n orignal:%@", error, orgStr); } - else NSLog(@"resp: %@\n%@", NSStringFromClass([data class]), [data jsonString]); + else NSLog(@"resp: %@\n%@", NSStringFromClass([data class]), [data h_jsonString]); }]; }]; @@ -85,7 +84,7 @@ - (instancetype)init dao.cacheType = [HNCacheTypeBoth createWtihCacheDuration:60]; [dao startWithQueueName:nil finish:^(id sender, id data, NSError *error) { if (error) NSLog(@"%@", error); - else NSLog(@"%@", [data jsonString]); + else NSLog(@"%@", [data h_jsonString]); }]; }]; @@ -94,19 +93,39 @@ - (instancetype)init DemoNetworkDAO *dao = [DemoNetworkDAO new]; dao.appkey = @"db5c321697d0fd38ce68988d5a28f97e"; dao.info = @"joke"; - dao.cacheType = [HNCacheTypeAlternative createWtihCacheDuration:60 nextRequstInterval:40]; + dao.cacheType = [HNCacheTypeAlternative createWtihNextRequstInterval:40]; [dao startWithQueueName:nil finish:^(id sender, id data, NSError *error) { - if (error) NSLog(@"%@", error); + if (data) NSLog(@"data = %@", data); + if (error) NSLog(@"error = %@", error); + + if (sender == nil) + { + NSLog(@"cache callback"); + } + else + { + NSLog(@"network callback"); + } + + }]; + }]; + + [self addMenu:@"HNCacheTypeUseCacheOnFail test" callback:^(id sender, id o) { + DemoNetworkDAO *dao = [DemoNetworkDAO new]; + dao.appkey = @"db5c321697d0fd38ce68988d5a28f97e"; + dao.info = @"joke"; + dao.cacheType = [HNCacheTypeUseCacheOnFail createWtihCacheDuration:300]; + [dao startWithQueueName:nil finish:^(id sender, id data, NSError *error) { + if (data) NSLog(@"data = %@", data); + if (error) NSLog(@"error = %@", error); + + if (sender == nil) + { + NSLog(@"cache callback"); + } else { - if (sender == nil) - { - NSLog(@"cache callback"); - } - else - { - NSLog(@"network callback"); - } + NSLog(@"network callback"); } }]; }]; @@ -115,12 +134,12 @@ - (instancetype)init HNetworkDAO *dao = [HNetworkDAO new]; dao.baseURL = @"http://img.hb.aicdn.com/30e26fbd16eafb928a8c4a4943ab7d0557a67d7714295-uhMVq2_fw658"; dao.isFileDownload = YES; - [dao setProgressBlock:^(NSUInteger bytesWritten, long long totalBytesWritten, long long totalBytesExpectedToWrite){ - NSLog(@"progress %lli, %lli", totalBytesWritten, totalBytesExpectedToWrite); + [dao setProgressBlock:^(id sender, double progress){ + NSLog(@"progress %f", progress); }]; [dao startWithQueueName:nil finish:^(id sender, id data, NSError *error) { if (error) NSLog(@"%@", error); - else NSLog(@"get file: %@", [data jsonString]); + else NSLog(@"get file: %@", [data h_jsonString]); }]; }]; @@ -130,7 +149,7 @@ - (instancetype)init dao.isFileDownload = YES; [dao startWithQueueName:nil finish:^(id sender, id data, NSError *error) { if (error) NSLog(@"%@", error); - else NSLog(@"get file: %@", [data jsonString]); + else NSLog(@"get file: %@", [data h_jsonString]); }]; }]; @@ -140,14 +159,31 @@ - (instancetype)init dao.deserializer = [HNEntityDeserializer deserializerWithClass:[TestEntity2 class]]; [dao startWithQueueName:nil finish:^(id sender, id data, NSError *error) { if (error) NSLog(@"%@", error); - else NSLog(@"resp: %@", [data jsonString]); + else NSLog(@"resp: %@", [data h_jsonString]); }]; }]; - [self addMenu:@"pbnetwork test" callback:^(id sender, id data) { - @strongify(self) - [self.navigationController pushViewController:[PBNetworkDaoTestVC new] animated:YES]; + [self addMenu:@"test json request" callback:^(id sender, id data) { + DemoJsonDao *dao = [DemoJsonDao new]; + dao.s = @"123"; + DemoJsonDaoEntity *obj = [DemoJsonDaoEntity new]; + obj.a = @"a"; + obj.b = 0; + obj.objs = @[CdemoJsonDaoEntity(@"b", 1), CdemoJsonDaoEntity(@"c", 2)]; + dao.obj = obj; + [dao startWithQueueName:nil finish:^(SimpleNetDAO *sender, id data, NSError *error) { + if (error) + { + NSString *orgStr = [[NSString alloc] initWithData:sender.responseData encoding:NSUTF8StringEncoding]; + NSLog(@"error: %@\n orignal: %@", error, orgStr); + } + else NSLog(@"resp: %@\n%@", NSStringFromClass([data class]), [data h_jsonString]); + }]; }]; +// [self addMenu:@"pbnetwork test" callback:^(id sender, id data) { +// @strongify(self) +// [self.navigationController pushViewController:[PBNetworkDaoTestVC new] animated:YES]; +// }]; } return self; @@ -176,7 +212,7 @@ - (UIView *)textBack if (!_textBack) { UIView *back = [[UIView alloc] initWithFrame:CGRectMake(0, self.view.height - 60, self.view.width, 60)]; - back.backgroundColor = [UIColor colorWithHex:0xf5f5f5]; + back.backgroundColor = [UIColor h_colorWithHex:0xf5f5f5]; back.autoresizingMask = UIViewAutoresizingFlexibleWidth; if (!_textView) { @@ -204,7 +240,7 @@ - (UIView *)textBack UIButton *btn = [UIButton buttonWithType:UIButtonTypeCustom]; btn.layer.cornerRadius = 5; - btn.backgroundColor = [UIColor colorWithHex:0x0066cc]; + btn.backgroundColor = [UIColor h_colorWithHex:0x0066cc]; btn.frame = CGRectMake(back.width - 60 - 10, (back.height - 44)/2, 60, 44); [btn setTintColor:[UIColor darkGrayColor]]; [btn setTitle:@"send" forState:UIControlStateNormal]; @@ -291,4 +327,28 @@ - (instancetype)init @implementation DemoEntity ppx(otherInfo, HPIgnore) -@end \ No newline at end of file +@end + + +@implementation DemoJsonDao +- (instancetype)init +{ + self = [super init]; + if (self) { + self.baseURL = @"https://www.fastmock.site"; + self.pathURL = @"mock/68e3baf7b104d3c372fed7a41937caa7/haccess/test/jsonrequest"; + self.method = @"POST"; + self.requstContentType = HNContentTypeApplicaitonJson; + } + return self; +} +@end + +@implementation DemoJsonDaoEntity +@end +DemoJsonDaoEntity *CdemoJsonDaoEntity(NSString *a, int b) { + DemoJsonDaoEntity *obj = [DemoJsonDaoEntity new]; + obj.a = a; + obj.b = b; + return obj; +} diff --git a/Demo/PBEntity/Person.pbobjc.h b/Demo/PBEntity/Person.pbobjc.h deleted file mode 100644 index 32af591..0000000 --- a/Demo/PBEntity/Person.pbobjc.h +++ /dev/null @@ -1,49 +0,0 @@ -// Generated by the protocol buffer compiler. DO NOT EDIT! -// source: Person.proto - -#import "GPBProtocolBuffers.h" - -#if GOOGLE_PROTOBUF_OBJC_GEN_VERSION != 30000 -#error This file was generated by a different version of protoc which is incompatible with your Protocol Buffer library sources. -#endif - -// @@protoc_insertion_point(imports) - -CF_EXTERN_C_BEGIN - -NS_ASSUME_NONNULL_BEGIN - -#pragma mark - PersonRoot - -@interface PersonRoot : GPBRootObject - -// The base class provides: -// + (GPBExtensionRegistry *)extensionRegistry; -// which is an GPBExtensionRegistry that includes all the extensions defined by -// this file and all files that it depends on. - -@end - -#pragma mark - Person - -typedef GPB_ENUM(Person_FieldNumber) { - Person_FieldNumber_Name = 1, - Person_FieldNumber_Uid = 2, - Person_FieldNumber_Email = 3, -}; - -@interface Person : GPBMessage - -@property(nonatomic, readwrite, copy, null_resettable) NSString *name; - -@property(nonatomic, readwrite) int32_t uid; - -@property(nonatomic, readwrite, copy, null_resettable) NSString *email; - -@end - -NS_ASSUME_NONNULL_END - -CF_EXTERN_C_END - -// @@protoc_insertion_point(global_scope) diff --git a/Demo/PBEntity/Person.pbobjc.m b/Demo/PBEntity/Person.pbobjc.m deleted file mode 100644 index e7cff9c..0000000 --- a/Demo/PBEntity/Person.pbobjc.m +++ /dev/null @@ -1,106 +0,0 @@ -// Generated by the protocol buffer compiler. DO NOT EDIT! -// source: Person.proto - -#import "GPBProtocolBuffers_RuntimeSupport.h" -#import "Person.pbobjc.h" -// @@protoc_insertion_point(imports) - -#pragma mark - PersonRoot - -@implementation PersonRoot - -@end - -#pragma mark - PersonRoot_FileDescriptor - -static GPBFileDescriptor *PersonRoot_FileDescriptor(void) { - // This is called by +initialize so there is no need to worry - // about thread safety of the singleton. - static GPBFileDescriptor *descriptor = NULL; - if (!descriptor) { - GPBDebugCheckRuntimeVersion(); - descriptor = [[GPBFileDescriptor alloc] initWithPackage:@"" - syntax:GPBFileSyntaxProto3]; - } - return descriptor; -} - -#pragma mark - Person - -@implementation Person - -@dynamic name; -@dynamic uid; -@dynamic email; - -typedef struct Person__storage_ { - uint32_t _has_storage_[1]; - int32_t uid; - NSString *name; - NSString *email; -} Person__storage_; - -// This method is threadsafe because it is initially called -// in +initialize for each subclass. -+ (GPBDescriptor *)descriptor { - static GPBDescriptor *descriptor = nil; - if (!descriptor) { - static GPBMessageFieldDescription fields[] = { - { - .name = "name", - .number = Person_FieldNumber_Name, - .hasIndex = 0, - .flags = GPBFieldOptional, - .dataType = GPBDataTypeString, - .offset = offsetof(Person__storage_, name), - .defaultValue.valueString = nil, - .dataTypeSpecific.className = NULL, - .fieldOptions = NULL, - }, - { - .name = "uid", - .number = Person_FieldNumber_Uid, - .hasIndex = 1, - .flags = GPBFieldOptional, - .dataType = GPBDataTypeInt32, - .offset = offsetof(Person__storage_, uid), - .defaultValue.valueInt32 = 0, - .dataTypeSpecific.className = NULL, - .fieldOptions = NULL, - }, - { - .name = "email", - .number = Person_FieldNumber_Email, - .hasIndex = 2, - .flags = GPBFieldOptional, - .dataType = GPBDataTypeString, - .offset = offsetof(Person__storage_, email), - .defaultValue.valueString = nil, - .dataTypeSpecific.className = NULL, - .fieldOptions = NULL, - }, - }; - GPBDescriptor *localDescriptor = - [GPBDescriptor allocDescriptorForClass:[Person class] - rootClass:[PersonRoot class] - file:PersonRoot_FileDescriptor() - fields:fields - fieldCount:sizeof(fields) / sizeof(GPBMessageFieldDescription) - oneofs:NULL - oneofCount:0 - enums:NULL - enumCount:0 - ranges:NULL - rangeCount:0 - storageSize:sizeof(Person__storage_) - wireFormat:NO]; - NSAssert(descriptor == nil, @"Startup recursed!"); - descriptor = localDescriptor; - } - return descriptor; -} - -@end - - -// @@protoc_insertion_point(global_scope) diff --git a/Demo/PBEntity/Photo.pbobjc.h b/Demo/PBEntity/Photo.pbobjc.h deleted file mode 100644 index 23a853a..0000000 --- a/Demo/PBEntity/Photo.pbobjc.h +++ /dev/null @@ -1,64 +0,0 @@ -// Generated by the protocol buffer compiler. DO NOT EDIT! -// source: Photo.proto - -#import "GPBProtocolBuffers.h" - -#if GOOGLE_PROTOBUF_OBJC_GEN_VERSION != 30000 -#error This file was generated by a different version of protoc which is incompatible with your Protocol Buffer library sources. -#endif - -// @@protoc_insertion_point(imports) - -CF_EXTERN_C_BEGIN - -NS_ASSUME_NONNULL_BEGIN - -#pragma mark - PhotoRoot - -@interface PhotoRoot : GPBRootObject - -// The base class provides: -// + (GPBExtensionRegistry *)extensionRegistry; -// which is an GPBExtensionRegistry that includes all the extensions defined by -// this file and all files that it depends on. - -@end - -#pragma mark - PhotoInfo - -typedef GPB_ENUM(PhotoInfo_FieldNumber) { - PhotoInfo_FieldNumber_Pid = 1, - PhotoInfo_FieldNumber_Width = 2, - PhotoInfo_FieldNumber_URL = 3, -}; - -@interface PhotoInfo : GPBMessage - -@property(nonatomic, readwrite, copy, null_resettable) NSString *pid; - -@property(nonatomic, readwrite) int32_t width; - -@property(nonatomic, readwrite, copy, null_resettable) NSString *uRL; - -@end - -#pragma mark - PhotoInfo_Etag - -typedef GPB_ENUM(PhotoInfo_Etag_FieldNumber) { - PhotoInfo_Etag_FieldNumber_Number = 1, - PhotoInfo_Etag_FieldNumber_Eid = 2, -}; - -@interface PhotoInfo_Etag : GPBMessage - -@property(nonatomic, readwrite, copy, null_resettable) NSString *number; - -@property(nonatomic, readwrite, copy, null_resettable) NSString *eid; - -@end - -NS_ASSUME_NONNULL_END - -CF_EXTERN_C_END - -// @@protoc_insertion_point(global_scope) diff --git a/Demo/PBEntity/Photo.pbobjc.m b/Demo/PBEntity/Photo.pbobjc.m deleted file mode 100644 index bc8ddbb..0000000 --- a/Demo/PBEntity/Photo.pbobjc.m +++ /dev/null @@ -1,176 +0,0 @@ -// Generated by the protocol buffer compiler. DO NOT EDIT! -// source: Photo.proto - -#import "GPBProtocolBuffers_RuntimeSupport.h" -#import "Photo.pbobjc.h" -// @@protoc_insertion_point(imports) - -#pragma mark - PhotoRoot - -@implementation PhotoRoot - -@end - -#pragma mark - PhotoRoot_FileDescriptor - -static GPBFileDescriptor *PhotoRoot_FileDescriptor(void) { - // This is called by +initialize so there is no need to worry - // about thread safety of the singleton. - static GPBFileDescriptor *descriptor = NULL; - if (!descriptor) { - GPBDebugCheckRuntimeVersion(); - descriptor = [[GPBFileDescriptor alloc] initWithPackage:@"" - syntax:GPBFileSyntaxProto3]; - } - return descriptor; -} - -#pragma mark - PhotoInfo - -@implementation PhotoInfo - -@dynamic pid; -@dynamic width; -@dynamic uRL; - -typedef struct PhotoInfo__storage_ { - uint32_t _has_storage_[1]; - int32_t width; - NSString *pid; - NSString *uRL; -} PhotoInfo__storage_; - -// This method is threadsafe because it is initially called -// in +initialize for each subclass. -+ (GPBDescriptor *)descriptor { - static GPBDescriptor *descriptor = nil; - if (!descriptor) { - static GPBMessageFieldDescription fields[] = { - { - .name = "pid", - .number = PhotoInfo_FieldNumber_Pid, - .hasIndex = 0, - .flags = GPBFieldOptional, - .dataType = GPBDataTypeString, - .offset = offsetof(PhotoInfo__storage_, pid), - .defaultValue.valueString = nil, - .dataTypeSpecific.className = NULL, - .fieldOptions = NULL, - }, - { - .name = "width", - .number = PhotoInfo_FieldNumber_Width, - .hasIndex = 1, - .flags = GPBFieldOptional, - .dataType = GPBDataTypeInt32, - .offset = offsetof(PhotoInfo__storage_, width), - .defaultValue.valueInt32 = 0, - .dataTypeSpecific.className = NULL, - .fieldOptions = NULL, - }, - { - .name = "uRL", - .number = PhotoInfo_FieldNumber_URL, - .hasIndex = 2, - .flags = GPBFieldOptional | GPBFieldTextFormatNameCustom, - .dataType = GPBDataTypeString, - .offset = offsetof(PhotoInfo__storage_, uRL), - .defaultValue.valueString = nil, - .dataTypeSpecific.className = NULL, - .fieldOptions = NULL, - }, - }; -#if GPBOBJC_SKIP_MESSAGE_TEXTFORMAT_EXTRAS - const char *extraTextFormatInfo = NULL; -#else - static const char *extraTextFormatInfo = "\001\003\001!!\000"; -#endif // GPBOBJC_SKIP_MESSAGE_TEXTFORMAT_EXTRAS - GPBDescriptor *localDescriptor = - [GPBDescriptor allocDescriptorForClass:[PhotoInfo class] - rootClass:[PhotoRoot class] - file:PhotoRoot_FileDescriptor() - fields:fields - fieldCount:sizeof(fields) / sizeof(GPBMessageFieldDescription) - oneofs:NULL - oneofCount:0 - enums:NULL - enumCount:0 - ranges:NULL - rangeCount:0 - storageSize:sizeof(PhotoInfo__storage_) - wireFormat:NO - extraTextFormatInfo:extraTextFormatInfo]; - NSAssert(descriptor == nil, @"Startup recursed!"); - descriptor = localDescriptor; - } - return descriptor; -} - -@end - -#pragma mark - PhotoInfo_Etag - -@implementation PhotoInfo_Etag - -@dynamic number; -@dynamic eid; - -typedef struct PhotoInfo_Etag__storage_ { - uint32_t _has_storage_[1]; - NSString *number; - NSString *eid; -} PhotoInfo_Etag__storage_; - -// This method is threadsafe because it is initially called -// in +initialize for each subclass. -+ (GPBDescriptor *)descriptor { - static GPBDescriptor *descriptor = nil; - if (!descriptor) { - static GPBMessageFieldDescription fields[] = { - { - .name = "number", - .number = PhotoInfo_Etag_FieldNumber_Number, - .hasIndex = 0, - .flags = GPBFieldOptional, - .dataType = GPBDataTypeString, - .offset = offsetof(PhotoInfo_Etag__storage_, number), - .defaultValue.valueString = nil, - .dataTypeSpecific.className = NULL, - .fieldOptions = NULL, - }, - { - .name = "eid", - .number = PhotoInfo_Etag_FieldNumber_Eid, - .hasIndex = 1, - .flags = GPBFieldOptional, - .dataType = GPBDataTypeString, - .offset = offsetof(PhotoInfo_Etag__storage_, eid), - .defaultValue.valueString = nil, - .dataTypeSpecific.className = NULL, - .fieldOptions = NULL, - }, - }; - GPBDescriptor *localDescriptor = - [GPBDescriptor allocDescriptorForClass:[PhotoInfo_Etag class] - rootClass:[PhotoRoot class] - file:PhotoRoot_FileDescriptor() - fields:fields - fieldCount:sizeof(fields) / sizeof(GPBMessageFieldDescription) - oneofs:NULL - oneofCount:0 - enums:NULL - enumCount:0 - ranges:NULL - rangeCount:0 - storageSize:sizeof(PhotoInfo_Etag__storage_) - wireFormat:NO]; - NSAssert(descriptor == nil, @"Startup recursed!"); - descriptor = localDescriptor; - } - return descriptor; -} - -@end - - -// @@protoc_insertion_point(global_scope) diff --git a/Demo/PBEntity/User.pbobjc.h b/Demo/PBEntity/User.pbobjc.h deleted file mode 100644 index a127f3e..0000000 --- a/Demo/PBEntity/User.pbobjc.h +++ /dev/null @@ -1,85 +0,0 @@ -// Generated by the protocol buffer compiler. DO NOT EDIT! -// source: User.proto - -#import "GPBProtocolBuffers.h" - -#if GOOGLE_PROTOBUF_OBJC_GEN_VERSION != 30000 -#error This file was generated by a different version of protoc which is incompatible with your Protocol Buffer library sources. -#endif - -// @@protoc_insertion_point(imports) - -CF_EXTERN_C_BEGIN - -NS_ASSUME_NONNULL_BEGIN - -#pragma mark - Enum UserInfo_PhoneType - -typedef GPB_ENUM(UserInfo_PhoneType) { - UserInfo_PhoneType_GPBUnrecognizedEnumeratorValue = kGPBUnrecognizedEnumeratorValue, - UserInfo_PhoneType_Mobile = 0, - UserInfo_PhoneType_Home = 1, - UserInfo_PhoneType_Work = 2, -}; - -GPBEnumDescriptor *UserInfo_PhoneType_EnumDescriptor(void); - -BOOL UserInfo_PhoneType_IsValidValue(int32_t value); - -#pragma mark - UserRoot - -@interface UserRoot : GPBRootObject - -// The base class provides: -// + (GPBExtensionRegistry *)extensionRegistry; -// which is an GPBExtensionRegistry that includes all the extensions defined by -// this file and all files that it depends on. - -@end - -#pragma mark - UserInfo - -typedef GPB_ENUM(UserInfo_FieldNumber) { - UserInfo_FieldNumber_Name = 1, - UserInfo_FieldNumber_Uid = 2, - UserInfo_FieldNumber_Email = 3, - UserInfo_FieldNumber_PhoneArray = 4, -}; - -@interface UserInfo : GPBMessage - -@property(nonatomic, readwrite, copy, null_resettable) NSString *name; - -@property(nonatomic, readwrite) int32_t uid; - -@property(nonatomic, readwrite, copy, null_resettable) NSString *email; - -// |phoneArray| contains |UserInfo_PhoneNumber| -@property(nonatomic, readwrite, strong, null_resettable) NSMutableArray *phoneArray; -@property(nonatomic, readonly) NSUInteger phoneArray_Count; - -@end - -#pragma mark - UserInfo_PhoneNumber - -typedef GPB_ENUM(UserInfo_PhoneNumber_FieldNumber) { - UserInfo_PhoneNumber_FieldNumber_Number = 1, - UserInfo_PhoneNumber_FieldNumber_Type = 2, -}; - -@interface UserInfo_PhoneNumber : GPBMessage - -@property(nonatomic, readwrite, copy, null_resettable) NSString *number; - -@property(nonatomic, readwrite) UserInfo_PhoneType type; - -@end - -int32_t UserInfo_PhoneNumber_Type_RawValue(UserInfo_PhoneNumber *message); -void SetUserInfo_PhoneNumber_Type_RawValue(UserInfo_PhoneNumber *message, int32_t value); - -NS_ASSUME_NONNULL_END - -CF_EXTERN_C_END - -// @@protoc_insertion_point(global_scope) diff --git a/Demo/PBEntity/User.pbobjc.m b/Demo/PBEntity/User.pbobjc.m deleted file mode 100644 index b969673..0000000 --- a/Demo/PBEntity/User.pbobjc.m +++ /dev/null @@ -1,227 +0,0 @@ -// Generated by the protocol buffer compiler. DO NOT EDIT! -// source: User.proto - -#import "GPBProtocolBuffers_RuntimeSupport.h" -#import "User.pbobjc.h" -// @@protoc_insertion_point(imports) - -#pragma mark - UserRoot - -@implementation UserRoot - -@end - -#pragma mark - UserRoot_FileDescriptor - -static GPBFileDescriptor *UserRoot_FileDescriptor(void) { - // This is called by +initialize so there is no need to worry - // about thread safety of the singleton. - static GPBFileDescriptor *descriptor = NULL; - if (!descriptor) { - GPBDebugCheckRuntimeVersion(); - descriptor = [[GPBFileDescriptor alloc] initWithPackage:@"" - syntax:GPBFileSyntaxProto3]; - } - return descriptor; -} - -#pragma mark - UserInfo - -@implementation UserInfo - -@dynamic name; -@dynamic uid; -@dynamic email; -@dynamic phoneArray, phoneArray_Count; - -typedef struct UserInfo__storage_ { - uint32_t _has_storage_[1]; - int32_t uid; - NSString *name; - NSString *email; - NSMutableArray *phoneArray; -} UserInfo__storage_; - -// This method is threadsafe because it is initially called -// in +initialize for each subclass. -+ (GPBDescriptor *)descriptor { - static GPBDescriptor *descriptor = nil; - if (!descriptor) { - static GPBMessageFieldDescription fields[] = { - { - .name = "name", - .number = UserInfo_FieldNumber_Name, - .hasIndex = 0, - .flags = GPBFieldOptional, - .dataType = GPBDataTypeString, - .offset = offsetof(UserInfo__storage_, name), - .defaultValue.valueString = nil, - .dataTypeSpecific.className = NULL, - .fieldOptions = NULL, - }, - { - .name = "uid", - .number = UserInfo_FieldNumber_Uid, - .hasIndex = 1, - .flags = GPBFieldOptional, - .dataType = GPBDataTypeInt32, - .offset = offsetof(UserInfo__storage_, uid), - .defaultValue.valueInt32 = 0, - .dataTypeSpecific.className = NULL, - .fieldOptions = NULL, - }, - { - .name = "email", - .number = UserInfo_FieldNumber_Email, - .hasIndex = 2, - .flags = GPBFieldOptional, - .dataType = GPBDataTypeString, - .offset = offsetof(UserInfo__storage_, email), - .defaultValue.valueString = nil, - .dataTypeSpecific.className = NULL, - .fieldOptions = NULL, - }, - { - .name = "phoneArray", - .number = UserInfo_FieldNumber_PhoneArray, - .hasIndex = GPBNoHasBit, - .flags = GPBFieldRepeated, - .dataType = GPBDataTypeMessage, - .offset = offsetof(UserInfo__storage_, phoneArray), - .defaultValue.valueMessage = nil, - .dataTypeSpecific.className = GPBStringifySymbol(UserInfo_PhoneNumber), - .fieldOptions = NULL, - }, - }; - static GPBMessageEnumDescription enums[] = { - { .enumDescriptorFunc = UserInfo_PhoneType_EnumDescriptor }, - }; - GPBDescriptor *localDescriptor = - [GPBDescriptor allocDescriptorForClass:[UserInfo class] - rootClass:[UserRoot class] - file:UserRoot_FileDescriptor() - fields:fields - fieldCount:sizeof(fields) / sizeof(GPBMessageFieldDescription) - oneofs:NULL - oneofCount:0 - enums:enums - enumCount:sizeof(enums) / sizeof(GPBMessageEnumDescription) - ranges:NULL - rangeCount:0 - storageSize:sizeof(UserInfo__storage_) - wireFormat:NO]; - NSAssert(descriptor == nil, @"Startup recursed!"); - descriptor = localDescriptor; - } - return descriptor; -} - -@end - -#pragma mark - Enum UserInfo_PhoneType - -GPBEnumDescriptor *UserInfo_PhoneType_EnumDescriptor(void) { - static GPBEnumDescriptor *descriptor = NULL; - if (!descriptor) { - static GPBMessageEnumValueDescription values[] = { - { .name = "Mobile", .number = UserInfo_PhoneType_Mobile }, - { .name = "Home", .number = UserInfo_PhoneType_Home }, - { .name = "Work", .number = UserInfo_PhoneType_Work }, - }; - descriptor = [GPBEnumDescriptor allocDescriptorForName:GPBNSStringifySymbol(UserInfo_PhoneType) - values:values - valueCount:sizeof(values) / sizeof(GPBMessageEnumValueDescription) - enumVerifier:UserInfo_PhoneType_IsValidValue]; - } - return descriptor; -} - -BOOL UserInfo_PhoneType_IsValidValue(int32_t value__) { - switch (value__) { - case UserInfo_PhoneType_Mobile: - case UserInfo_PhoneType_Home: - case UserInfo_PhoneType_Work: - return YES; - default: - return NO; - } -} - -#pragma mark - UserInfo_PhoneNumber - -@implementation UserInfo_PhoneNumber - -@dynamic number; -@dynamic type; - -typedef struct UserInfo_PhoneNumber__storage_ { - uint32_t _has_storage_[1]; - UserInfo_PhoneType type; - NSString *number; -} UserInfo_PhoneNumber__storage_; - -// This method is threadsafe because it is initially called -// in +initialize for each subclass. -+ (GPBDescriptor *)descriptor { - static GPBDescriptor *descriptor = nil; - if (!descriptor) { - static GPBMessageFieldDescription fields[] = { - { - .name = "number", - .number = UserInfo_PhoneNumber_FieldNumber_Number, - .hasIndex = 0, - .flags = GPBFieldOptional, - .dataType = GPBDataTypeString, - .offset = offsetof(UserInfo_PhoneNumber__storage_, number), - .defaultValue.valueString = nil, - .dataTypeSpecific.className = NULL, - .fieldOptions = NULL, - }, - { - .name = "type", - .number = UserInfo_PhoneNumber_FieldNumber_Type, - .hasIndex = 1, - .flags = GPBFieldOptional | GPBFieldHasEnumDescriptor, - .dataType = GPBDataTypeEnum, - .offset = offsetof(UserInfo_PhoneNumber__storage_, type), - .defaultValue.valueEnum = UserInfo_PhoneType_Mobile, - .dataTypeSpecific.enumDescFunc = UserInfo_PhoneType_EnumDescriptor, - .fieldOptions = NULL, - }, - }; - GPBDescriptor *localDescriptor = - [GPBDescriptor allocDescriptorForClass:[UserInfo_PhoneNumber class] - rootClass:[UserRoot class] - file:UserRoot_FileDescriptor() - fields:fields - fieldCount:sizeof(fields) / sizeof(GPBMessageFieldDescription) - oneofs:NULL - oneofCount:0 - enums:NULL - enumCount:0 - ranges:NULL - rangeCount:0 - storageSize:sizeof(UserInfo_PhoneNumber__storage_) - wireFormat:NO]; - NSAssert(descriptor == nil, @"Startup recursed!"); - descriptor = localDescriptor; - } - return descriptor; -} - -@end - -int32_t UserInfo_PhoneNumber_Type_RawValue(UserInfo_PhoneNumber *message) { - GPBDescriptor *descriptor = [UserInfo_PhoneNumber descriptor]; - GPBFieldDescriptor *field = [descriptor fieldWithNumber:UserInfo_PhoneNumber_FieldNumber_Type]; - return GPBGetMessageInt32Field(message, field); -} - -void SetUserInfo_PhoneNumber_Type_RawValue(UserInfo_PhoneNumber *message, int32_t value) { - GPBDescriptor *descriptor = [UserInfo_PhoneNumber descriptor]; - GPBFieldDescriptor *field = [descriptor fieldWithNumber:UserInfo_PhoneNumber_FieldNumber_Type]; - GPBSetInt32IvarWithFieldInternal(message, field, value, descriptor.file.syntax); -} - - -// @@protoc_insertion_point(global_scope) diff --git a/Demo/PBNetworkDaoTestVC.h b/Demo/PBNetworkDaoTestVC.h deleted file mode 100644 index 061f6d8..0000000 --- a/Demo/PBNetworkDaoTestVC.h +++ /dev/null @@ -1,23 +0,0 @@ -// -// PBNetworkDaoTestVC.h -// HAccess -// -// Created by goingta on 16/3/11. -// Copyright © 2016年 zhangchutian. All rights reserved. -// - -#import -#import "HTestVC.h" - -@interface PBNetworkDaoTestVC : HTestVC - -@end - - -#import "HNetworkDAO.h" - -@interface PBDemoNetworkDAO : HNetworkDAO -@property (nonatomic) NSString *appkey; -@property (nonatomic) NSString *info; -@property (nonatomic) NSString *userid; -@end \ No newline at end of file diff --git a/Demo/PBNetworkDaoTestVC.m b/Demo/PBNetworkDaoTestVC.m deleted file mode 100644 index b802660..0000000 --- a/Demo/PBNetworkDaoTestVC.m +++ /dev/null @@ -1,229 +0,0 @@ -// -// PBNetworkDaoTestVC.m -// HAccess -// -// Created by goingta on 16/3/11. -// Copyright © 2016年 zhangchutian. All rights reserved. -// - -#import "PBNetworkDaoTestVC.h" -#import "HTextInput/HTextField.h" -#import "HTextInput/HTextView.h" -#import "HTextInput/HTextInputMotherBoard.h" -#import "HTextInput/HTextAnimationBottom.h" -#import "HTextInput/HTextAnimationPosition.h" -#import "HCommon.h" -//#import "Person.pb.h" -#import "Person.pbobjc.h" -#import "HNPBDeseriralizer.h" -#import "PBTestNetworkDAO.h" -#import "User.pbobjc.h" -#import "Photo.pbobjc.h" - - -@interface PBNetworkDaoTestVC() - -@property (nonatomic) UIView *textBack; -@property (nonatomic) HTextField *textView; - -@end - -@implementation PBNetworkDaoTestVC - -- (instancetype)init -{ - self = [super init]; - if (self) { - self.view.backgroundColor = [UIColor whiteColor]; - self.title = @"PBNetworkDAO TEST"; - - [self addMenu:@"Simple Request" callback:^(id sender, id data) { - - PBSimpleNetDAO *dao = [PBSimpleNetDAO new]; - dao.mobile = @"18628140435"; - [dao startWithQueueName:nil finish:^(PBSimpleNetDAO *sender, id data, NSError *error) { - if (error) - { - NSString *orgStr = [[NSString alloc] initWithData:sender.responseData encoding:NSUTF8StringEncoding]; - NSLog(@"error: %@\n orignal: %@", error, orgStr); - } - else NSLog(@"resp: %@\n%@", NSStringFromClass([data class]), [data jsonString]); - }]; - }]; - - [self addMenu:@"Resp is Array" callback:^(id sender, id data) { - PBArraryNetDAO *dao = [PBArraryNetDAO new]; - [dao startWithQueueName:nil finish:^(PBArraryNetDAO *sender, id data, NSError *error) { - if (error) - { - NSString *orgStr = [[NSString alloc] initWithData:sender.responseData encoding:NSUTF8StringEncoding]; - NSLog(@"error: %@\n orignal:%@", error, orgStr); - } - else NSLog(@"resp: %@\n%@", NSStringFromClass([data class]), [data jsonString]); - }]; - }]; - - [self addMenu:@"manual deserializtion" callback:^(id sender, id data) { - PBManualNetDAO *dao = [PBManualNetDAO new]; - [dao startWithQueueName:nil finish:^(PBManualNetDAO *sender, id data, NSError *error) { - if (error) - { - NSString *orgStr = [[NSString alloc] initWithData:sender.responseData encoding:NSUTF8StringEncoding]; - NSLog(@"error: %@\n orignal:%@", error, orgStr); - } - else NSLog(@"resp: %@\n%@", NSStringFromClass([data class]), [data jsonString]); - }]; - }]; - - } - return self; -} - -- (void)viewDidLoad -{ - UIImageView *bg = [[UIImageView alloc] initWithImage:img(@"bg.jpg")]; - bg.frame = self.view.bounds; - ALWAYS_FULL(bg); - bg.contentMode = UIViewContentModeScaleAspectFit; - [self.view addSubview:bg]; - - [super viewDidLoad]; - self.tableView.backgroundColor = [UIColor clearColor]; - - - self.automaticallyAdjustsScrollViewInsets = NO; - [self.view addSubview:self.textBack]; - - - -} -- (UIView *)textBack -{ - if (!_textBack) - { - UIView *back = [[UIView alloc] initWithFrame:CGRectMake(0, self.view.height - 60, self.view.width, 60)]; - back.backgroundColor = [UIColor colorWithHex:0xf5f5f5]; - back.autoresizingMask = UIViewAutoresizingFlexibleWidth; - if (!_textView) - { - HTextField *textView = [[HTextField alloc] initWithFrame:CGRectMake(10, (back.height - 44)/2, back.width - 20 - 60 - 5, 44)]; - textView.placeholder = @"say something"; - textView.font = [UIFont systemFontOfSize:24]; - textView.layer.cornerRadius = 4; - textView.backgroundColor = [UIColor whiteColor]; - textView.autoresizingMask = UIViewAutoresizingFlexibleWidth; - [back addSubview:textView]; - HTextAnimationBottom *animation1 = [HTextAnimationBottom new]; - animation1.animationView = self.tableView; - animation1.inputView = textView; - __weak typeof(back) weakBack = back; - [animation1 setAdjustDistanceCallback: ^float(float distance){ - return distance + weakBack.height; - }]; - HTextAnimationPosition *animation2 = [HTextAnimationPosition new]; - animation2.animationView = back; - animation2.inputView = textView; - textView.animations = @[animation1, animation2]; - _textView = textView; - } - - - UIButton *btn = [UIButton buttonWithType:UIButtonTypeCustom]; - btn.layer.cornerRadius = 5; - btn.backgroundColor = [UIColor colorWithHex:0x0066cc]; - btn.frame = CGRectMake(back.width - 60 - 10, (back.height - 44)/2, 60, 44); - [btn setTintColor:[UIColor darkGrayColor]]; - [btn setTitle:@"send" forState:UIControlStateNormal]; - btn.titleLabel.font = [UIFont boldSystemFontOfSize:16]; - [back addSubview:btn]; - btn.autoresizingMask = UIViewAutoresizingFlexibleLeftMargin; - [btn addTarget:self action:@selector(send) forControlEvents:UIControlEventTouchUpInside]; - _textBack = back; - } - return _textBack; -} -- (void)send -{ - UITextField *textView = self.textView; - NSString *msg = [textView text]; - if (msg.length > 0) - { - [self sendMsg:msg textView:textView]; - } -} -- (void)sendMsg:(NSString *)msg textView:(UITextField *)textView -{ - //proto3 -// -// PhotoInfo *photo = [[PhotoInfo alloc] init]; -// photo.pid = @"12344555"; -// photo.uRL = @"http://b.hiphotos.baidu.com/image/h%3D200/sign=8c8dc382d239b60052ce08b7d9513526/b58f8c5494eef01fa36ad8a4e7fe9925bc317d51.jpg"; -// photo.width = 230; -// -// -// NSData *data = [photo data]; -// -// NSString *path = @"/Users/tanglei/Desktop/PhotoInfo.pb"; -// [data writeToFile:path atomically:YES]; -// -// NSData *ldata = [NSData dataWithContentsOfFile:path]; -// PhotoInfo *p = [PhotoInfo parseFromData:ldata error:nil]; -// NSLog(@"\nname:%@\nemail:%@\nuid:%d", p.name, p.email, p.uid); - - - PBDemoNetworkDAO *dao = [PBDemoNetworkDAO new]; - dao.appkey = @"db5c321697d0fd38ce68988d5a28f97e"; - dao.info = msg; - - @weakify(self) - [dao startWithQueueName:nil sucess:^(id sender, Person *data) { - @strongify(self) - UIToolbar *toolBar = [[UIToolbar alloc] initWithFrame:self.view.bounds]; - HTextView *textView = [[HTextView alloc] initWithFrame:CGRectMake(0, 0, self.view.width, self.view.height)]; - textView.backgroundColor = [UIColor clearColor]; - textView.font = [UIFont systemFontOfSize:16]; - textView.textColor = [UIColor blackColor]; - textView.text = [NSString stringWithFormat:@"\nname:%@\nemail:%@\nuid:%d", data.name, data.email, data.uid]; - textView.contentInset = UIEdgeInsetsMake(20 + 64, 20, 20, 20); - [toolBar addSubview:textView]; - - toolBar.alpha = 0; - [self.view insertSubview:toolBar belowSubview:self.textBack]; - [UIView animateWithDuration:0.2 animations:^{ - toolBar.alpha = 1; - }]; - - dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(3 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ - [UIView animateWithDuration:0.2 animations:^{ - toolBar.alpha = 0; - } completion:^(BOOL finished) { - [toolBar removeFromSuperview]; - }]; - }); - - } failure:^(id sender, NSError *error) { - - NSLog(@"error:%@", [error localizedDescription]); - - }]; -} - -@end - -@implementation PBDemoNetworkDAO - -- (instancetype)init -{ - self = [super init]; - if (self) { - self.baseURL = @"http://www.tuling123.com"; - self.pathURL = @"openapi/api"; - //#ifdef DEBUG - self.isMock = YES; - //#endif - self.deserializer = [HNPBDeseriralizer deserializerWithClass:[Person class]]; - } - return self; -} - -@end diff --git a/Demo/PBTestNetworkDAO.h b/Demo/PBTestNetworkDAO.h deleted file mode 100644 index 32de13e..0000000 --- a/Demo/PBTestNetworkDAO.h +++ /dev/null @@ -1,21 +0,0 @@ -// -// PBTestNetworkDAO.h -// HAccess -// -// Created by goingta on 16/3/12. -// Copyright © 2016年 zhangchutian. All rights reserved. -// - -#import -#import "HNetworkDAO.h" - -@interface PBSimpleNetDAO : HNetworkDAO -@property (nonatomic) NSString *mobile; -@end - -@interface PBArraryNetDAO : HNetworkDAO -@end - -@interface PBManualNetDAO : HNetworkDAO - -@end \ No newline at end of file diff --git a/Demo/PBTestNetworkDAO.m b/Demo/PBTestNetworkDAO.m deleted file mode 100644 index 8cfe728..0000000 --- a/Demo/PBTestNetworkDAO.m +++ /dev/null @@ -1,83 +0,0 @@ -// -// PBTestNetworkDAO.m -// HAccess -// -// Created by goingta on 16/3/12. -// Copyright © 2016年 zhangchutian. All rights reserved. -// - -#import "PBTestNetworkDAO.h" -#import "HNPBDeseriralizer.h" -#import "Person.pbobjc.h" -#import "NSString+ext.h" -#import "NSError+ext.h" -#import "User.pbobjc.h" -#import "Photo.pbobjc.h" - -@implementation PBSimpleNetDAO - -- (instancetype)init -{ - self = [super init]; - if (self) { - self.baseURL = @"http://apis.baidu.com"; - self.pathURL = @"apistore/mobilenumber/mobilenumber"; - //3 -// self.deserializeKeyPath = @"retData"; - self.isMock = YES; - //4 - self.deserializer = [HNPBDeseriralizer deserializerWithClass:[Person class]]; - } - return self; -} -- (void)didSetupParams:(NSMutableDictionary *)params -{ - NSMutableString *paramsStr = [NSMutableString new]; - //compute a sign code - NSArray *allKeys = [params keysSortedByValueUsingComparator:^NSComparisonResult(id _Nonnull obj1, id _Nonnull obj2) { - return [obj1 compare:obj2]; - }]; - for (NSString *key in allKeys) - { - [paramsStr appendFormat:@"%@%@",key, params[key]]; - } - NSString *md5String = [paramsStr md5]; - [params setValue:md5String forKey:@"sign"]; -} - -@end - -@implementation PBArraryNetDAO - -- (instancetype)init -{ - self = [super init]; - if (self) - { - self.baseURL = @"http://apis.baidu.com"; - self.pathURL = @"apistore/mobilenumber/mobilenumber"; - self.isMock = YES; - self.deserializer = [HNPBDeseriralizer deserializerWithClass:[UserInfo class]]; - } - return self; -} - -@end - -@implementation PBManualNetDAO - -- (instancetype)init -{ - self = [super init]; - if (self) - { - self.baseURL = @"http://netsdk-test.camera360.cn";//http://netsdk-test.camera360.cn/api/demo - self.pathURL = @"api/demo"; - // self.isMock = YES; - self.deserializer = [HNPBDeseriralizer deserializerWithClass:[PhotoInfo class]]; - } - - return self; -} - -@end \ No newline at end of file diff --git a/Demo/TestEntity1.h b/Demo/TestEntity1.h index e0a8a8f..d473c68 100644 --- a/Demo/TestEntity1.h +++ b/Demo/TestEntity1.h @@ -55,6 +55,7 @@ @interface TestEntity5 : HEntity @property (nonatomic) NSString *a; @property (nonatomic) NSArray *b; +@property (nonatomic, readonly) int c; @end @@ -115,4 +116,4 @@ @interface TestEntity11 : HEntity @property (nonatomic) long server_created; @property (nonatomic) long server_modified; -@end \ No newline at end of file +@end diff --git a/Demo/TestEntity1.m b/Demo/TestEntity1.m index 6c0853a..a6bd9e6 100644 --- a/Demo/TestEntity1.m +++ b/Demo/TestEntity1.m @@ -41,6 +41,9 @@ @implementation TestEntity4 @end @implementation TestEntity5 +- (int)c { + return 5; +} @end @@ -111,4 +114,4 @@ @implementation TestEntity11 ppx(server_modified, HPMapto(@"modified")) ppx(created, HPIgnore) ppx(modified, HPIgnore) -@end \ No newline at end of file +@end diff --git a/HAccess.podspec b/HAccess.podspec index 829e91b..80f6b3b 100644 --- a/HAccess.podspec +++ b/HAccess.podspec @@ -1,11 +1,11 @@ Pod::Spec.new do |s| - s.name = "HAccess" - s.version = "2.1.8" - s.summary = "A short description of HAccess." + s.name = "HAccess" + s.version = "3.1.5" + s.summary = "A short description of HAccess." - s.description = <<-DESC + s.description = <<-DESC A longer description of HAccess in Markdown format. * Think: Why did you write this? What is the focus? What does it do? @@ -14,44 +14,43 @@ Pod::Spec.new do |s| * Finally, don't worry about the indent, CocoaPods strips it! DESC - s.homepage = "https://github.com/jumperb/HAccess" + s.homepage = "https://github.com/jumperb/HAccess" - s.license = "Copyright" + s.license = "Copyright" - s.author = { "jumperb" => "zhangchutian_05@163.com" } + s.author = { "jumperb" => "zhangchutian_05@163.com" } - s.source = { :git => "https://github.com/jumperb/HAccess.git", :tag => s.version.to_s} + s.source = { :git => "https://github.com/jumperb/HAccess.git", :tag => s.version.to_s} s.requires_arc = true - s.ios.deployment_target = '7.0' + s.ios.deployment_target = '9.0' s.default_subspec = 'Network' s.subspec 'Entity' do |ss| - ss.dependency 'Hodor' + ss.dependency "Hodor/Defines" + ss.dependency "Hodor/Feature" + ss.dependency "Hodor/NS-Category" ss.ios.source_files = 'Classes/Entity/*.{h,m,mm,cpp,c}' end s.subspec 'Network' do |ss| - ss.dependency 'Hodor' - ss.dependency 'AFNetworking' ,'~>2.0' + ss.dependency "Hodor/Defines" + ss.dependency "Hodor/Feature" + ss.dependency "Hodor/NS-Category" + ss.dependency 'AFNetworking' ss.dependency 'HCache' ss.dependency 'HAccess/Entity' ss.ios.source_files = 'Classes/Network/*.{h,m,mm,cpp,c}' end s.subspec 'Database' do |ss| - ss.dependency 'Hodor' + ss.dependency "Hodor/Defines" + ss.dependency "Hodor/Feature" + ss.dependency "Hodor/NS-Category" ss.dependency 'FMDB' ss.dependency 'HAccess/Entity' ss.ios.source_files = 'Classes/Database/*.{h,m,mm,cpp,c}' end - - s.subspec 'Network+Protobuf' do |ss| - ss.dependency 'HAccess/Network' - ss.dependency 'protocol-for-objectivec' - ss.ios.source_files = 'Classes/Network+Protobuf/**/*.{h,m,mm,cpp,c}' - end - end diff --git a/HAccess.xcodeproj/project.pbxproj b/HAccess.xcodeproj/project.pbxproj index db983d5..2b58b31 100644 --- a/HAccess.xcodeproj/project.pbxproj +++ b/HAccess.xcodeproj/project.pbxproj @@ -23,7 +23,6 @@ 002D866E1B465BFA00D1D729 /* HDBMgr.m in Sources */ = {isa = PBXBuildFile; fileRef = 002D86621B465BFA00D1D729 /* HDBMgr.m */; }; 002D866F1B465BFA00D1D729 /* HEntity.m in Sources */ = {isa = PBXBuildFile; fileRef = 002D86651B465BFA00D1D729 /* HEntity.m */; }; 002D86781B46616300D1D729 /* NetworkDaoTestVC.m in Sources */ = {isa = PBXBuildFile; fileRef = 002D86771B46616300D1D729 /* NetworkDaoTestVC.m */; }; - 00350FEB1C9FDD7E00CDF06B /* HNProvider_AF2.m in Sources */ = {isa = PBXBuildFile; fileRef = 00350FEA1C9FDD7E00CDF06B /* HNProvider_AF2.m */; }; 00350FEE1C9FE27300CDF06B /* HNetworkMultiDataObj.m in Sources */ = {isa = PBXBuildFile; fileRef = 00350FED1C9FE27300CDF06B /* HNetworkMultiDataObj.m */; }; 00769C111BA866980022878B /* HPropertyMgr.m in Sources */ = {isa = PBXBuildFile; fileRef = 00769C101BA866980022878B /* HPropertyMgr.m */; }; 00786CFB1B96D0F4005A8247 /* TestEntity1.m in Sources */ = {isa = PBXBuildFile; fileRef = 00786CFA1B96D0F4005A8247 /* TestEntity1.m */; }; @@ -37,18 +36,13 @@ 00DD89241B462D6600214FD3 /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 00DD89231B462D6600214FD3 /* AppDelegate.m */; }; 00DD892C1B462D6600214FD3 /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 00DD892B1B462D6600214FD3 /* Images.xcassets */; }; 00E907851BAD11F600E10652 /* DeserializeDemo.m in Sources */ = {isa = PBXBuildFile; fileRef = 00E907841BAD11F600E10652 /* DeserializeDemo.m */; }; + 152761A9221697EF007EE5E2 /* NSObject+HDeserializable.m in Sources */ = {isa = PBXBuildFile; fileRef = 152761A8221697EF007EE5E2 /* NSObject+HDeserializable.m */; }; + 15FC2F011ED2BA91005E8B7B /* HNProvider_AF3.m in Sources */ = {isa = PBXBuildFile; fileRef = 15FC2F001ED2BA91005E8B7B /* HNProvider_AF3.m */; }; 9E68062C1C9BEBB700519539 /* HNetworkDAO.m in Sources */ = {isa = PBXBuildFile; fileRef = 9E6806241C9BEBB700519539 /* HNetworkDAO.m */; }; 9E68062D1C9BEBB700519539 /* HNQueueManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 9E6806261C9BEBB700519539 /* HNQueueManager.m */; }; 9E68062E1C9BEBB700519539 /* HNJsonDeserializer.m in Sources */ = {isa = PBXBuildFile; fileRef = 9E6806281C9BEBB700519539 /* HNJsonDeserializer.m */; }; - 9E68062F1C9BEBB700519539 /* HNPBDeseriralizer.m in Sources */ = {isa = PBXBuildFile; fileRef = 9E68062B1C9BEBB700519539 /* HNPBDeseriralizer.m */; }; - 9EF847661C92637900202D0E /* PBNetworkDaoTestVC.m in Sources */ = {isa = PBXBuildFile; fileRef = 9EF847651C92637900202D0E /* PBNetworkDaoTestVC.m */; }; - 9EF847701C92680400202D0E /* Person.pbobjc.m in Sources */ = {isa = PBXBuildFile; fileRef = 9EF8476F1C92680400202D0E /* Person.pbobjc.m */; settings = {COMPILER_FLAGS = "-fno-objc-arc"; }; }; 9EF8822A1C8816D300238751 /* HNetworkDAO.bundle in Resources */ = {isa = PBXBuildFile; fileRef = 9EF882291C8816D300238751 /* HNetworkDAO.bundle */; }; - 9EFFCCC91C945F96008764E8 /* PBTestNetworkDAO.m in Sources */ = {isa = PBXBuildFile; fileRef = 9EFFCCC81C945F96008764E8 /* PBTestNetworkDAO.m */; }; - 9EFFCCCF1C950BC7008764E8 /* User.pbobjc.m in Sources */ = {isa = PBXBuildFile; fileRef = 9EFFCCCE1C950BC7008764E8 /* User.pbobjc.m */; settings = {COMPILER_FLAGS = "-fno-objc-arc"; }; }; - 9EFFCCD21C950FEA008764E8 /* Photo.pbobjc.m in Sources */ = {isa = PBXBuildFile; fileRef = 9EFFCCD11C950FEA008764E8 /* Photo.pbobjc.m */; settings = {COMPILER_FLAGS = "-fno-objc-arc"; }; }; - B233A7CDA81ACF4EA36DFB69 /* libPods.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 99CAB5AA2A23FAC518F6E98C /* libPods.a */; }; - CC2B65C4C3A52CEA191D0E0A /* libPods-HAccess.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 6603FF90984091327174E3B6 /* libPods-HAccess.a */; }; + AB73FA1E1B2DF049FECC1E41 /* libPods-HAccess.a in Frameworks */ = {isa = PBXBuildFile; fileRef = A193E4158595155F021CF283 /* libPods-HAccess.a */; }; /* End PBXBuildFile section */ /* Begin PBXFileReference section */ @@ -82,8 +76,6 @@ 002D86761B46616300D1D729 /* NetworkDaoTestVC.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = NetworkDaoTestVC.h; sourceTree = ""; }; 002D86771B46616300D1D729 /* NetworkDaoTestVC.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = NetworkDaoTestVC.m; sourceTree = ""; }; 00350FE81C9FD61A00CDF06B /* HNetworkProvider.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = HNetworkProvider.h; sourceTree = ""; }; - 00350FE91C9FDD7E00CDF06B /* HNProvider_AF2.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = HNProvider_AF2.h; sourceTree = ""; }; - 00350FEA1C9FDD7E00CDF06B /* HNProvider_AF2.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = HNProvider_AF2.m; sourceTree = ""; }; 00350FEC1C9FE27300CDF06B /* HNetworkMultiDataObj.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = HNetworkMultiDataObj.h; sourceTree = ""; }; 00350FED1C9FE27300CDF06B /* HNetworkMultiDataObj.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = HNetworkMultiDataObj.m; sourceTree = ""; }; 00769C0F1BA866980022878B /* HPropertyMgr.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = HPropertyMgr.h; sourceTree = ""; }; @@ -110,11 +102,12 @@ 00DD892B1B462D6600214FD3 /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Images.xcassets; sourceTree = ""; }; 00E907831BAD11F600E10652 /* DeserializeDemo.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DeserializeDemo.h; sourceTree = ""; }; 00E907841BAD11F600E10652 /* DeserializeDemo.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = DeserializeDemo.m; sourceTree = ""; }; - 2EE6650205A1F6B0AFE04968 /* Pods-HAccess.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-HAccess.debug.xcconfig"; path = "Pods/Target Support Files/Pods-HAccess/Pods-HAccess.debug.xcconfig"; sourceTree = ""; }; - 63DFD70F3DB6B40DCC91924E /* Pods.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = Pods.release.xcconfig; path = "Pods/Target Support Files/Pods/Pods.release.xcconfig"; sourceTree = ""; }; - 6603FF90984091327174E3B6 /* libPods-HAccess.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-HAccess.a"; sourceTree = BUILT_PRODUCTS_DIR; }; - 8B3F5E34E5262575100D707F /* Pods.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = Pods.debug.xcconfig; path = "Pods/Target Support Files/Pods/Pods.debug.xcconfig"; sourceTree = ""; }; - 99CAB5AA2A23FAC518F6E98C /* libPods.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libPods.a; sourceTree = BUILT_PRODUCTS_DIR; }; + 0413D743DE52FC56EE813F8C /* Pods-HAccess.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-HAccess.debug.xcconfig"; path = "Pods/Target Support Files/Pods-HAccess/Pods-HAccess.debug.xcconfig"; sourceTree = ""; }; + 0E64B8DBCAB9BDE77B441E0F /* Pods-HAccess.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-HAccess.release.xcconfig"; path = "Pods/Target Support Files/Pods-HAccess/Pods-HAccess.release.xcconfig"; sourceTree = ""; }; + 152761A7221697EF007EE5E2 /* NSObject+HDeserializable.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "NSObject+HDeserializable.h"; sourceTree = ""; }; + 152761A8221697EF007EE5E2 /* NSObject+HDeserializable.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = "NSObject+HDeserializable.m"; sourceTree = ""; }; + 15FC2EFF1ED2BA91005E8B7B /* HNProvider_AF3.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = HNProvider_AF3.h; sourceTree = ""; }; + 15FC2F001ED2BA91005E8B7B /* HNProvider_AF3.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = HNProvider_AF3.m; sourceTree = ""; }; 9E6806221C9BEBB700519539 /* HNDeserializer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = HNDeserializer.h; sourceTree = ""; }; 9E6806231C9BEBB700519539 /* HNetworkDAO.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = HNetworkDAO.h; sourceTree = ""; }; 9E6806241C9BEBB700519539 /* HNetworkDAO.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = HNetworkDAO.m; sourceTree = ""; }; @@ -122,20 +115,8 @@ 9E6806261C9BEBB700519539 /* HNQueueManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = HNQueueManager.m; sourceTree = ""; }; 9E6806271C9BEBB700519539 /* HNJsonDeserializer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = HNJsonDeserializer.h; sourceTree = ""; }; 9E6806281C9BEBB700519539 /* HNJsonDeserializer.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = HNJsonDeserializer.m; sourceTree = ""; }; - 9E68062A1C9BEBB700519539 /* HNPBDeseriralizer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = HNPBDeseriralizer.h; sourceTree = ""; }; - 9E68062B1C9BEBB700519539 /* HNPBDeseriralizer.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = HNPBDeseriralizer.m; sourceTree = ""; }; - 9EF847641C92637900202D0E /* PBNetworkDaoTestVC.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PBNetworkDaoTestVC.h; sourceTree = ""; }; - 9EF847651C92637900202D0E /* PBNetworkDaoTestVC.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PBNetworkDaoTestVC.m; sourceTree = ""; }; - 9EF8476E1C92680400202D0E /* Person.pbobjc.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Person.pbobjc.h; sourceTree = ""; }; - 9EF8476F1C92680400202D0E /* Person.pbobjc.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = Person.pbobjc.m; sourceTree = ""; }; 9EF882291C8816D300238751 /* HNetworkDAO.bundle */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.plug-in"; path = HNetworkDAO.bundle; sourceTree = ""; }; - 9EFFCCC71C945F96008764E8 /* PBTestNetworkDAO.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PBTestNetworkDAO.h; sourceTree = ""; }; - 9EFFCCC81C945F96008764E8 /* PBTestNetworkDAO.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PBTestNetworkDAO.m; sourceTree = ""; }; - 9EFFCCCD1C950BC7008764E8 /* User.pbobjc.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = User.pbobjc.h; sourceTree = ""; }; - 9EFFCCCE1C950BC7008764E8 /* User.pbobjc.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = User.pbobjc.m; sourceTree = ""; }; - 9EFFCCD01C950FEA008764E8 /* Photo.pbobjc.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Photo.pbobjc.h; sourceTree = ""; }; - 9EFFCCD11C950FEA008764E8 /* Photo.pbobjc.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = Photo.pbobjc.m; sourceTree = ""; }; - A8703F6331B0159074372311 /* Pods-HAccess.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-HAccess.release.xcconfig"; path = "Pods/Target Support Files/Pods-HAccess/Pods-HAccess.release.xcconfig"; sourceTree = ""; }; + A193E4158595155F021CF283 /* libPods-HAccess.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-HAccess.a"; sourceTree = BUILT_PRODUCTS_DIR; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -143,8 +124,7 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( - B233A7CDA81ACF4EA36DFB69 /* libPods.a in Frameworks */, - CC2B65C4C3A52CEA191D0E0A /* libPods-HAccess.a in Frameworks */, + AB73FA1E1B2DF049FECC1E41 /* libPods-HAccess.a in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -194,10 +174,6 @@ 002D86771B46616300D1D729 /* NetworkDaoTestVC.m */, 000C9B261C90326C009D6EE0 /* TestNetworkDAO.h */, 000C9B271C90326C009D6EE0 /* TestNetworkDAO.m */, - 9EF847641C92637900202D0E /* PBNetworkDaoTestVC.h */, - 9EF847651C92637900202D0E /* PBNetworkDaoTestVC.m */, - 9EFFCCC71C945F96008764E8 /* PBTestNetworkDAO.h */, - 9EFFCCC81C945F96008764E8 /* PBTestNetworkDAO.m */, ); name = network; sourceTree = ""; @@ -205,7 +181,6 @@ 002D862C1B462EA300D1D729 /* Demo */ = { isa = PBXGroup; children = ( - 9EF847671C92650300202D0E /* PBEntity */, 002438A11C30E108003F3CF9 /* network */, 002438A01C30E0F3003F3CF9 /* deserial */, 0024389F1C30E0D4003F3CF9 /* dbtest */, @@ -221,7 +196,6 @@ isa = PBXGroup; children = ( 9E6806211C9BEBB700519539 /* Network */, - 9E6806291C9BEBB700519539 /* Network+Protobuf */, 002D865E1B465BFA00D1D729 /* Database */, 002D86631B465BFA00D1D729 /* Entity */, ); @@ -250,6 +224,8 @@ 002D86651B465BFA00D1D729 /* HEntity.m */, 00769C0F1BA866980022878B /* HPropertyMgr.h */, 00769C101BA866980022878B /* HPropertyMgr.m */, + 152761A7221697EF007EE5E2 /* NSObject+HDeserializable.h */, + 152761A8221697EF007EE5E2 /* NSObject+HDeserializable.m */, ); path = Entity; sourceTree = ""; @@ -274,8 +250,8 @@ 002D862C1B462EA300D1D729 /* Demo */, 00DD891D1B462D6600214FD3 /* HAccess */, 00DD891C1B462D6600214FD3 /* Products */, - 6AB15CECE928247E4185CCCC /* Pods */, - 77E818DC0ADCB480589DA6AE /* Frameworks */, + C18AB030D5136AF45B6F8EB0 /* Pods */, + 4D2A90F8734D5929AB2B9B9A /* Frameworks */, ); sourceTree = ""; }; @@ -307,22 +283,10 @@ name = "Supporting Files"; sourceTree = ""; }; - 6AB15CECE928247E4185CCCC /* Pods */ = { + 4D2A90F8734D5929AB2B9B9A /* Frameworks */ = { isa = PBXGroup; children = ( - 8B3F5E34E5262575100D707F /* Pods.debug.xcconfig */, - 63DFD70F3DB6B40DCC91924E /* Pods.release.xcconfig */, - 2EE6650205A1F6B0AFE04968 /* Pods-HAccess.debug.xcconfig */, - A8703F6331B0159074372311 /* Pods-HAccess.release.xcconfig */, - ); - name = Pods; - sourceTree = ""; - }; - 77E818DC0ADCB480589DA6AE /* Frameworks */ = { - isa = PBXGroup; - children = ( - 99CAB5AA2A23FAC518F6E98C /* libPods.a */, - 6603FF90984091327174E3B6 /* libPods-HAccess.a */, + A193E4158595155F021CF283 /* libPods-HAccess.a */, ); name = Frameworks; sourceTree = ""; @@ -341,40 +305,27 @@ 9E6806271C9BEBB700519539 /* HNJsonDeserializer.h */, 9E6806281C9BEBB700519539 /* HNJsonDeserializer.m */, 00350FE81C9FD61A00CDF06B /* HNetworkProvider.h */, - 00350FE91C9FDD7E00CDF06B /* HNProvider_AF2.h */, - 00350FEA1C9FDD7E00CDF06B /* HNProvider_AF2.m */, + 15FC2EFF1ED2BA91005E8B7B /* HNProvider_AF3.h */, + 15FC2F001ED2BA91005E8B7B /* HNProvider_AF3.m */, ); path = Network; sourceTree = ""; }; - 9E6806291C9BEBB700519539 /* Network+Protobuf */ = { - isa = PBXGroup; - children = ( - 9E68062A1C9BEBB700519539 /* HNPBDeseriralizer.h */, - 9E68062B1C9BEBB700519539 /* HNPBDeseriralizer.m */, - ); - path = "Network+Protobuf"; - sourceTree = ""; - }; - 9EF847671C92650300202D0E /* PBEntity */ = { + 9EF882261C8814F400238751 /* Mock */ = { isa = PBXGroup; children = ( - 9EFFCCD01C950FEA008764E8 /* Photo.pbobjc.h */, - 9EFFCCD11C950FEA008764E8 /* Photo.pbobjc.m */, - 9EFFCCCD1C950BC7008764E8 /* User.pbobjc.h */, - 9EFFCCCE1C950BC7008764E8 /* User.pbobjc.m */, - 9EF8476E1C92680400202D0E /* Person.pbobjc.h */, - 9EF8476F1C92680400202D0E /* Person.pbobjc.m */, + 9EF882291C8816D300238751 /* HNetworkDAO.bundle */, ); - path = PBEntity; + path = Mock; sourceTree = ""; }; - 9EF882261C8814F400238751 /* Mock */ = { + C18AB030D5136AF45B6F8EB0 /* Pods */ = { isa = PBXGroup; children = ( - 9EF882291C8816D300238751 /* HNetworkDAO.bundle */, + 0413D743DE52FC56EE813F8C /* Pods-HAccess.debug.xcconfig */, + 0E64B8DBCAB9BDE77B441E0F /* Pods-HAccess.release.xcconfig */, ); - path = Mock; + name = Pods; sourceTree = ""; }; /* End PBXGroup section */ @@ -384,12 +335,10 @@ isa = PBXNativeTarget; buildConfigurationList = 00DD893E1B462D6600214FD3 /* Build configuration list for PBXNativeTarget "HAccess" */; buildPhases = ( - C710932FBCB45639246F26F9 /* [CP] Check Pods Manifest.lock */, + 92E4E7FA8231818972F6277D /* [CP] Check Pods Manifest.lock */, 00DD89171B462D6600214FD3 /* Sources */, 00DD89181B462D6600214FD3 /* Frameworks */, 00DD89191B462D6600214FD3 /* Resources */, - 1D0782E0E2B469A1C4C0A3E5 /* [CP] Copy Pods Resources */, - 0D08BA5C7287FA781D90C7FF /* [CP] Embed Pods Frameworks */, ); buildRules = ( ); @@ -419,6 +368,7 @@ developmentRegion = English; hasScannedForEncodings = 0; knownRegions = ( + English, en, Base, ); @@ -449,49 +399,22 @@ /* End PBXResourcesBuildPhase section */ /* Begin PBXShellScriptBuildPhase section */ - 0D08BA5C7287FA781D90C7FF /* [CP] Embed Pods Frameworks */ = { - isa = PBXShellScriptBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - inputPaths = ( - ); - name = "[CP] Embed Pods Frameworks"; - outputPaths = ( - ); - runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-HAccess/Pods-HAccess-frameworks.sh\"\n"; - showEnvVarsInLog = 0; - }; - 1D0782E0E2B469A1C4C0A3E5 /* [CP] Copy Pods Resources */ = { - isa = PBXShellScriptBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - inputPaths = ( - ); - name = "[CP] Copy Pods Resources"; - outputPaths = ( - ); - runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-HAccess/Pods-HAccess-resources.sh\"\n"; - showEnvVarsInLog = 0; - }; - C710932FBCB45639246F26F9 /* [CP] Check Pods Manifest.lock */ = { + 92E4E7FA8231818972F6277D /* [CP] Check Pods Manifest.lock */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; files = ( ); inputPaths = ( + "${PODS_PODFILE_DIR_PATH}/Podfile.lock", + "${PODS_ROOT}/Manifest.lock", ); name = "[CP] Check Pods Manifest.lock"; outputPaths = ( + "$(DERIVED_FILE_DIR)/Pods-HAccess-checkManifestLockResult.txt", ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; - shellScript = "diff \"${PODS_ROOT}/../Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n"; + shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; showEnvVarsInLog = 0; }; /* End PBXShellScriptBuildPhase section */ @@ -503,14 +426,11 @@ files = ( 002D864D1B462EA300D1D729 /* Boss.m in Sources */, 002D86571B462EA300D1D729 /* User.m in Sources */, - 00350FEB1C9FDD7E00CDF06B /* HNProvider_AF2.m in Sources */, 9E68062D1C9BEBB700519539 /* HNQueueManager.m in Sources */, 002D864E1B462EA300D1D729 /* BossLocalDao.m in Sources */, - 9EF847701C92680400202D0E /* Person.pbobjc.m in Sources */, 002D86561B462EA300D1D729 /* StudentLocalDao.m in Sources */, 00A005701CA9149800C1E213 /* HNSystemCacheStrategy.m in Sources */, 00E907851BAD11F600E10652 /* DeserializeDemo.m in Sources */, - 9E68062F1C9BEBB700519539 /* HNPBDeseriralizer.m in Sources */, 9E68062E1C9BEBB700519539 /* HNJsonDeserializer.m in Sources */, 002D866F1B465BFA00D1D729 /* HEntity.m in Sources */, 002D86781B46616300D1D729 /* NetworkDaoTestVC.m in Sources */, @@ -522,19 +442,17 @@ 002D865C1B465ABC00D1D729 /* MenuVC.m in Sources */, 9E68062C1C9BEBB700519539 /* HNetworkDAO.m in Sources */, 00DD89241B462D6600214FD3 /* AppDelegate.m in Sources */, - 9EFFCCC91C945F96008764E8 /* PBTestNetworkDAO.m in Sources */, 00DC44621C9BAD940036282E /* HEntity+Persistence.m in Sources */, 002D866D1B465BFA00D1D729 /* HDatabaseDAO.m in Sources */, - 9EFFCCCF1C950BC7008764E8 /* User.pbobjc.m in Sources */, 00769C111BA866980022878B /* HPropertyMgr.m in Sources */, 00786CFB1B96D0F4005A8247 /* TestEntity1.m in Sources */, 00A005691CA90D6400C1E213 /* HAccess2DBSource.m in Sources */, 00A0056F1CA9149800C1E213 /* HNCustomCacheStrategy.m in Sources */, 00DD89211B462D6600214FD3 /* main.m in Sources */, + 15FC2F011ED2BA91005E8B7B /* HNProvider_AF3.m in Sources */, 00350FEE1C9FE27300CDF06B /* HNetworkMultiDataObj.m in Sources */, + 152761A9221697EF007EE5E2 /* NSObject+HDeserializable.m in Sources */, 002D86581B462EA300D1D729 /* UserLocalDao.m in Sources */, - 9EF847661C92637900202D0E /* PBNetworkDaoTestVC.m in Sources */, - 9EFFCCD21C950FEA008764E8 /* Photo.pbobjc.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -625,10 +543,11 @@ }; 00DD893F1B462D6600214FD3 /* Debug */ = { isa = XCBuildConfiguration; - baseConfigurationReference = 2EE6650205A1F6B0AFE04968 /* Pods-HAccess.debug.xcconfig */; + baseConfigurationReference = 0413D743DE52FC56EE813F8C /* Pods-HAccess.debug.xcconfig */; buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; ASSETCATALOG_COMPILER_LAUNCHIMAGE_NAME = LaunchImage; + CLANG_ANALYZER_NONNULL = YES; FRAMEWORK_SEARCH_PATHS = ( "$(inherited)", "$(PROJECT_DIR)/Demo/objectivec", @@ -645,7 +564,7 @@ "\"${PODS_ROOT}/Headers/Public/Hodor\"", ); INFOPLIST_FILE = HAccess/Info.plist; - IPHONEOS_DEPLOYMENT_TARGET = 7.0; + IPHONEOS_DEPLOYMENT_TARGET = 15.6; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; LIBRARY_SEARCH_PATHS = ( "$(inherited)", @@ -659,10 +578,11 @@ }; 00DD89401B462D6600214FD3 /* Release */ = { isa = XCBuildConfiguration; - baseConfigurationReference = A8703F6331B0159074372311 /* Pods-HAccess.release.xcconfig */; + baseConfigurationReference = 0E64B8DBCAB9BDE77B441E0F /* Pods-HAccess.release.xcconfig */; buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; ASSETCATALOG_COMPILER_LAUNCHIMAGE_NAME = LaunchImage; + CLANG_ANALYZER_NONNULL = YES; FRAMEWORK_SEARCH_PATHS = ( "$(inherited)", "$(PROJECT_DIR)/Demo/objectivec", @@ -679,7 +599,7 @@ "\"${PODS_ROOT}/Headers/Public/Hodor\"", ); INFOPLIST_FILE = HAccess/Info.plist; - IPHONEOS_DEPLOYMENT_TARGET = 7.0; + IPHONEOS_DEPLOYMENT_TARGET = 15.6; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; LIBRARY_SEARCH_PATHS = ( "$(inherited)", diff --git a/HAccess.xcodeproj/xcuserdata/issuser.xcuserdatad/xcschemes/xcschememanagement.plist b/HAccess.xcodeproj/xcuserdata/issuser.xcuserdatad/xcschemes/xcschememanagement.plist new file mode 100644 index 0000000..81fcc16 --- /dev/null +++ b/HAccess.xcodeproj/xcuserdata/issuser.xcuserdatad/xcschemes/xcschememanagement.plist @@ -0,0 +1,14 @@ + + + + + SchemeUserState + + HAccess.xcscheme_^#shared#^_ + + orderHint + 7 + + + + diff --git a/HAccess.xcodeproj/xcuserdata/zct.xcuserdatad/xcschemes/xcschememanagement.plist b/HAccess.xcodeproj/xcuserdata/zct.xcuserdatad/xcschemes/xcschememanagement.plist new file mode 100644 index 0000000..81fcc16 --- /dev/null +++ b/HAccess.xcodeproj/xcuserdata/zct.xcuserdatad/xcschemes/xcschememanagement.plist @@ -0,0 +1,14 @@ + + + + + SchemeUserState + + HAccess.xcscheme_^#shared#^_ + + orderHint + 7 + + + + diff --git a/HAccess.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/HAccess.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 0000000..18d9810 --- /dev/null +++ b/HAccess.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/HAccess.xcworkspace/xcuserdata/chutianzhang.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist b/HAccess.xcworkspace/xcuserdata/chutianzhang.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist index 8d3cd35..311ed53 100644 --- a/HAccess.xcworkspace/xcuserdata/chutianzhang.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist +++ b/HAccess.xcworkspace/xcuserdata/chutianzhang.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist @@ -32,31 +32,31 @@ shouldBeEnabled = "Yes" ignoreCount = "0" continueAfterRunningActions = "No" - symbolName = "__33-[HFileCache backgroundCleanDisk]_block_invoke" + symbolName = "-[HFileCache _getSize]" moduleName = "HAccess" usesParentBreakpointCondition = "Yes" - urlString = "file:///Users/chutianzhang/Documents/github_pods/HAccess/Pods/HCache/Classes/HFileCache.m" - timestampString = "493982376.828783" + urlString = "file:///Users/chutianzhang/Documents/pods/HAccess/Pods/HCache/Classes/HFileCache.m" + timestampString = "514802178.12854" startingColumnNumber = "9223372036854775807" endingColumnNumber = "9223372036854775807" startingLineNumber = "240" endingLineNumber = "240" - offsetFromSymbolStart = "88"> + offsetFromSymbolStart = "695"> + offsetFromSymbolStart = "724"> @@ -74,7 +74,7 @@ startingLineNumber = "746" endingLineNumber = "746" landmarkName = "-entityPropertyDetailList:isDepSearch:" - landmarkType = "5"> + landmarkType = "7"> + landmarkType = "7"> + landmarkType = "7"> + startingLineNumber = "510" + endingLineNumber = "510" + landmarkName = "-requestFinishedSucessWithInfo:response:" + landmarkType = "7"> - - - - + startingLineNumber = "165" + endingLineNumber = "165" + landmarkName = "-testDBEntity" + landmarkType = "7"> diff --git a/HAccess.xcworkspace/xcuserdata/zct.xcuserdatad/IDEFindNavigatorScopes.plist b/HAccess.xcworkspace/xcuserdata/zct.xcuserdatad/IDEFindNavigatorScopes.plist new file mode 100644 index 0000000..5dd5da8 --- /dev/null +++ b/HAccess.xcworkspace/xcuserdata/zct.xcuserdatad/IDEFindNavigatorScopes.plist @@ -0,0 +1,5 @@ + + + + + diff --git a/HAccess.xcworkspace/xcuserdata/zct.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist b/HAccess.xcworkspace/xcuserdata/zct.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist new file mode 100644 index 0000000..e857f74 --- /dev/null +++ b/HAccess.xcworkspace/xcuserdata/zct.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist @@ -0,0 +1,6 @@ + + + diff --git a/Podfile b/Podfile index fb44f9d..6cefefd 100644 --- a/Podfile +++ b/Podfile @@ -1,12 +1,12 @@ -platform :ios, :deployment_target => "7.0" -source 'https://github.com/CocoaPods/Specs.git' +platform :ios, :deployment_target => "9.0" +source 'https://cdn.cocoapods.org/' target 'HAccess' do - pod "AFNetworking",'~>2.0' + pod "AFNetworking/NSURLSession" pod "FMDB" pod "Hodor" pod "HTestVC" pod "HTextInput" pod "HCache" - pod "protocol-for-objectivec" +# pod "protocol-for-objectivec" end diff --git a/README.md b/README.md index 68024f1..1ccf7e5 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,533 @@ -# HAccess -this a a middle layer between rude data (local data,networking data) and biz or controller +# What? +HAccess是介于一个网络数据,数据库数据与业务层之间的一个库,主要目的是降低业务层对这些数据的访问成本。 +在大多数情况下,可以直接不需要业务层,直接通过controller层即可快速访问数据了。 +库主要由HDeserializableObject,HNetworkDAO和HDatabaseDAO构成 +### HNetworkDAO 特性如下 +* 面向对象的接口管理方式 +* 请求参数映射 +* 极简的反序列化支持 +* 具有模型验证能力 +* 请求生命周期可扩展 +* 极简的队列控制 +* 极简的缓存控制 +* 极简的mock功能 + +### HDatabaseDAO 特性如下 +TODO + + +# How? + + +## 一.文件下载 +``` objectivec +HNetworkDAO *dao = [HNetworkDAO new]; +dao.baseURL = @"https://uploadbeta.com/api/pictures/random/?key=%E6%8E%A8%E5%A5%B3%E9%83%8E"; +dao.isFileDownload = YES; //注释1 +[dao start:^(id sender, id data) { + NSLog(@"数据是%@", [data jsonString]);//注释2 +} failure:^(id sender, NSError *error) { + NSLog(@"error:%@", error.localizedDescription); +}]; +``` + +//注释1 由于这里是文件下载,所以必须指明是文件下载,目前还没支持根据响应的content-type来决定返回啥东西,主要是AFNetworking还没支持 +//注释2 jsonString在 #import 这个里面 + +如果有报错 +``` +错误:Error Domain=Network Code=-1022 "The resource could not be loaded because the App Transport Security policy requires the use of a secure connection." UserInfo={NSLocalizedDescription=The resource could not be loaded because the App Transport Security policy requires the use of a secure connection.} +``` +在info.plist里面添加 +``` +NSAppTransportSecurity + + NSAllowsArbitraryLoads + + +``` +下载好了,获取到返回 +``` +{ + "length" : 49181, + "suggestedFilename" : "random.jpeg", + "MIMEType" : "image\/jpeg", + "filePath" : "\/Users\/zct\/Library\/Developer\/CoreSimulator\/Devices\/19BED3C5-5BBF-49E7-A82B-8BC7ED436B1C\/data\/Containers\/Data\/Application\/AF88FB0F-E5CE-40B6-BFCE-9C685EAE7CFF\/Library\/Caches\/com.hacess.HFileCache\/1570A0716FA3979787A9EA27BB253518" +} +``` + +这是一个内置的数据结构 +```objectivec +/** + * file download info + * if HNetworkDAO.isFileDownload is YES, you will recv a response of this type if successed + * once you have downloaded, move the file to other place, otherwise it will be deleted after 1 minute + */ +@interface HDownloadFileInfo : NSObject +@property (nonatomic) NSString *filePath; +@property (nonatomic) NSString *MIMEType; +@property (nonatomic) long long length; +@property (nonatomic) NSString *suggestedFilename; +@end +``` +直接拿着用就好了, +**注意:**如果需要存储的话一定要移走,因为放在原地的话,1小时候会被删除 +关于com.hacess.HFileCache这个,我们在后面的文章讲 + +可以看出这是一个图片下载,接着我们把这段代码复制一份做成一个函数,以便后面的demo使用 +``` objectivec +- (void)showImageURL:(NSString *)url +{ + HNetworkDAO *dao = [HNetworkDAO new]; + dao.baseURL = url; + dao.isFileDownload = YES; + [dao start:^(id sender, HDownloadFileInfo *data) { + UIImageView *imageView = [[UIImageView alloc] initWithFrame:self.view.bounds]; + imageView.contentMode = UIViewContentModeScaleAspectFit; + imageView.image = [UIImage imageWithContentsOfFile:data.filePath]; + [self.view addSubview:imageView]; + + dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(3 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ + [imageView removeFromSuperview]; + }); + } failure:^(id sender, NSError *error) { + NSLog(@"error:%@", error.localizedDescription); + }]; +} +``` + + +## 二.简单接口调用 + +接口api https://uploadbeta.com/picture-gallery/faq.php#api 的search接口 +首先,HAccess建议使用面向对象的代码组织方式,所有的请求类,模型类都是在一个继承关系中的 + +创建SimpleDAO作为HNetworkDAO的子类 +```objectivec +#import +@interface SimpleDAO : HNetworkDAO +@property (nonatomic) NSString *searchKey; +@end +``` +```objectivec +@implementation SimpleDAO +ppx(searchKey, HPMapto(@"key")) +- (instancetype)init +{ + self = [super init]; + if (self) { + self.baseURL = @"https://uploadbeta.com"; + self.pathURL = @"/api/pictures/search/?start=1&offset=10"; + } + return self; +} +@end +``` +测试一下 +```objectivec +[self addMenu:@"简单请求" callback:^(id sender, id data) { + SimpleDAO *dao = [SimpleDAO new]; + dao.searchKey = @"sexy"; + [dao start:^(id sender, id data, NSError *error) { + NSLog(@"data:%@",data); + }]; +}]; +``` + +不出意外,你会获得一个字典返回 + +**注意:** “.m”文件里面有个ppx描述,这种写法叫做“属性注解”,这是一个属性描述方法,这句话的意思是说,把ID映射到id,为啥要映射啊? +A.服务器参数有objective-c的保留字 +B.服务端参数名太随意,而我们有洁癖 +C.如果服务端要日怪,非要把参数放在 http header 里面,还可以这样 +```objectivec +ppx(searchKey, HPMapto(@"key"), HPHeader) +``` + +## 三.简单反序列化 + +SimpleDAO下发下来应该是个图集对象,我们希望直接能获取到一个直接可用的业务模型 +我们先根据返回数据编写模型,可以直接写在SimpleDAO里面,我们期望返回的模型如下定义 +```objectivec +@interface ImageObject : HDeserializableObject +@property (nonatomic) NSString *ID; +@property (nonatomic) NSString *url; +@property (nonatomic) NSString *title; +@property (nonatomic) NSString *fullImageURL; +@end +``` +```objectivec +@implementation ImageObject +- (NSString *)fullImageURL +{ + if (!_fullImageURL) + { + _fullImageURL = [@"https://uploadbeta.com/_s/" stringByAppendingString:self.url]; + } + return _fullImageURL; +} +@end + +``` +为了顺利进行反序列化,我们让他都继承自HDeserializableObject +并且在SimpleDAO的init函数中添加如下语句,告知DAO需要反序列化成什么 +```objectivec +self.deserializer = [HNArrayDeserializer deserializerWithClass:[ImageObject class]]; +``` +HNArrayDeserializer 是指一种反序列化为数组的反序列化器,反序列化器内置只有有三种: + +HNEntityDeserializer(反序列化为对象,支持对象的嵌套), +HNArrayDeserializer(反序列化为数组), +HNManualDeserializer(人工反序列化) 就能满足所有的情况。 + +再试一试? + +你会依次遇到几个问题,后面是解决方式 + +`1.ImageObject的属性叫ID 但是数据的key叫id` + +将头文件的id改成大写然后做属性映射 +ppx(ID, HPMapto(@"id”)) + +`2.服务端下发的id是数字,但是我们要求是字符串,怎么办?` +ppx(ID, HPMapto(@"id"), HPAutoCast) //AutoCast是指字符串和数字可以按需自动转换 + +`3.框架报了ImageObject的fullImageURL不能为空,那个只是客户端使用的,不需要转化的` +ppx(fullImageURL, HPIgnore) //忽略掉 + +整体代码如下 +```objectivec +@implementation ImageObject +ppx(ID, HPMapto(@"id"), HPAutoCast) +ppx(fullImageURL, HPIgnore) +- (NSString *)fullImageURL +{ + if (!_fullImageURL) + { + _fullImageURL = [@"https://uploadbeta.com/_s/" stringByAppendingString:self.url]; + } + return _fullImageURL; +} +@end +``` + + +## 四.服务端错误处理 + +错误分为三种级别: + +* 1.从NSURLSession来的错误,从AFNetworking来的错误,这种错误的Domain是“Network” +例如,http的404,500等 + +* 2.服务端返回业务错误码,这种错误的Domain是“Server”,例如 +{"code":"100002","info":"系统忙,请稍候再试"} + +* 3.服务端的数据不合格,被客户端检查出来了 +这种错误的Domain是识别到错误的模块,code是“kDataFormatErrorCode”,例如json解码失败,数据字段缺失等,例如"com.haccess.HNJsonDeserializer.HNEntityDeserializer,ImageObject:ID must be @NSString type" + +其中第一种和第三种都是自动处理了,第二种需要代码来检测,框架不知道哪个代表成功哪个代表错误 +例如如下的返回结果 +```objectivec +{"msg":"访问权限错误","status":false} +``` +我们会在json解析完成后来检查数据是不是代表错误,检查代码写在哪儿呢? +首先HNetworkDAO有个生命周期,类似UIViewController,这些都是一些请求过程的关键环节,可以在子类中自定义 +```objectivec +#pragma mark - life circle +//设置请求头部 +- (void)setupHeader:(NSMutableDictionary *)headers; +//设置参数 +- (id)setupParams; +//设置参数字典 +- (void)setupParams:(NSMutableDictionary *)params; +//设置参数完成 +- (void)didSetupParams:(NSMutableDictionary *)params; +//将要发送请求,带上下文参数的 +- (void)willSendRequest:(NSString *)urlString method:(NSString *)method headers:(NSMutableDictionary *)headers params:(NSMutableDictionary *)params; +//将要发送请求 +- (void)willSendRequest:(NSMutableURLRequest *)request; +//开始请求 +- (void)startWithQueueName:(NSString*)queueName; +//获得响应 +- (void)requestFinishedSucessWithInfo:(NSData *)responInfo response:(NSURLResponse *)response; +//获取响应对象 +- (id)getOutputEntiy:(id)responseObject; +//获取到错误 +- (void)requestFinishedFailureWithError:(NSError*)error; +``` + +我们需要做的服务器错误处理就发生在"getOutputEntiy"这个环节 +复写这个方法 +```objectivec +- (id)getOutputEntiy:(id)responseObject +{ + if (![responseObject isKindOfClass:[NSDictionary class]]) return herr(kDataFormatErrorCode, @"预期响应格式是一个字典"); + NSDictionary *responseDict = (NSDictionary *)responseObject; + if (responseDict[@"status"] && [responseDict[@"status"] boolValue] && !responseDict[@"msg"]) + { + return [super getOutputEntiy:responseObject]; + } + else + { + NSString *msg = responseDict[@"msg"]; + if (!msg) msg = @"服务端错误"; + return herr(503, msg); + } +} +``` +这样我们就可以把错误报到上层去了 +Error Domain=SimpleDAO.m Code=503 "访问权限错误!" UserInfo={NSLocalizedDescription=访问权限错误! + +## 五.生命周期的作用 + +我们来做个简单的签名 +签名算法:所有请求参数,把key按照升序排列,数据按照a=1&b=2的方式连接成串,追加"123456"进行MD5得到sign,然后再通过sign传到服务器 +```objectivec +- (NSString *)sign:(NSDictionary *)params +{ + NSArray *keys = [params keysSortedByValueUsingComparator:^NSComparisonResult(id _Nonnull obj1, id _Nonnull obj2) { + return [obj1 compare:obj2]; + }]; + NSMutableString *signString = [NSMutableString new]; + for (NSString *key in keys) + { + if (signString.length == 0) + { + [signString appendFormat:@"%@=%@", key, params[key]]; + } + else + { + [signString appendFormat:@"&%@=%@", key, params[key]]; + } + } + [signString appendString:@"123456"]; + return [signString md5]; +} +``` +那么在哪儿写入呢? +```objectivec +- (void)willSendRequest:(NSString *)urlString method:(NSString *)method headers:(NSMutableDictionary *)headers params:(NSMutableDictionary *)params +{ + [super willSendRequest:urlString method:method headers:headers params:params]; + params[@"sign"] = [self sign:params]; +} +``` +好了,发个请求试试吧 + +## 7.复杂反序列化 + +#### 7.1 反序列化路径 +如果数据层次较复杂,SimpleDAO是不知道你关心那一部分数据的,这里就需要指定反序列化路径了,例如 +``` +{ + "resp":{ + "data":{ + //我关心的数据 + } + } + "code":123, + "msg":"成功" +} +``` +反序列化路径就这样写 +```objectivec +self.deserializeKeyPath = @"resp.data"; +``` + + +#### 7.2 反序列化选项 + +我们之前已经接触到了几个反序列化选项 +`HPMapto`: 属性映射关系 +`HPIgnore`: 忽略这个属性,不要对这个属性进行反序列化 +`HPAutoCast`: 字符串和数字自动按需转换 + +然而还有四个选项没告诉大家 + +`HPOptional`: 这个属性可以为空 +`HPInnerType`: 指明内部类型,常用于指明数组内部是啥类型 +`HPScope`: 这个属性的值域是什么,一般用于描述枚举类型 +`HPDivideType`: 类型分离,用于决定数组内部类型或者字典对应类型,例如数组中的数据 +```json +[ + { + "type":"pic", + "picUrl":"http://www.abc.jpg", + "picW":450, + "picH":450 + }, + { + "type":"video", + "videoUrl":"http://www.drf.mp4", + "videoW":450, + "videoH":450 + } +] +``` +需要将 type == pic 的字典转换成 PicObject, type == video 则转换成 VideoObject +那么就可以这样写 +``` +ppx(某个属性, HPDivideType(@"type", "pic", [PicObject class], "video", [VideoObject class])) +``` +除了字符串 ,这里还可以填写 @(1), @(枚举值) 作为type的值 + +记住这七个选项的作用,需要用到的时候再来查看Demo中的用法 +注意: 反序列化选项`对DAO的属性同样适用`,例如,你定义的属性名和请求参数的名字可以不一样!并通过HPMapto转换过去 + + +## 七.队列控制 + +在上文中我们已经能获取到一个图片数组了,我们现在想要把图片显示出来 +请求那儿改成这样 +```objectivec +SimpleDAO *dao = [SimpleDAO new]; +dao.searchKey = @"sexy"; +[dao start:^(id sender, NSArray *data, NSError *error) { + @strongify(self) + if (error) + { + NSLog(@"%@",error); + } + else + { + for (ImageObject *imgObj in data) + { + [self showImageURL:[imgObj fullImageURL]]; + } + } +}]; +``` +这时我们发现图片有时候会同时回来,前一张会把后一张盖住,原因是下载图片的请求我们是同时发出去的。 +我们可以让请求排队执行,以解决这个问题 + +#### 7.1并发控制 + +我们修改一下showImageURL这个函数 +```objectivec +- (void)showImageURL:(NSString *)url +{ + HNetworkDAO *dao = [HNetworkDAO new]; + dao.baseURL = url; + dao.isFileDownload = YES; + [dao startWithQueueName:@"imageDownloadQueue" sucess:^(id sender, HDownloadFileInfo *data) { + UIImageView *imageView = [[UIImageView alloc] initWithFrame:self.view.bounds]; + imageView.contentMode = UIViewContentModeScaleAspectFit; + imageView.image = [UIImage imageWithContentsOfFile:data.filePath]; + [self.view addSubview:imageView]; + + dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(3 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ + [imageView removeFromSuperview]; + }); + } failure:^(id sender, NSError *error) { + NSLog(@"error:%@", error.localizedDescription); + }]; +} +``` +观察图片回来的log,就可以发现他是一张一张回来的了, 原因是我们给他指明了一个队列imageDownloadQueue,默认情况下这个是个串行的 +如果希望设置并发数量,可以在请求开始之前设置 +```objectivec +[HNetworkDAO initQueueWithName:@"imageDownloadQueue" maxMaxConcurrent:2]; +``` +#### 7.2队列回调 + +我们希望在所有图片下载完成后得到一个通知 +```objectivec +@strongify(self) +[HNetworkDAO initQueueWithName:@"imageDownloadQueue" maxMaxConcurrent:2]; + +for (ImageObject *imgObj in data) +{ + [self showImageURL:[imgObj imageURL]]; +} + +[HNetworkDAO queue:@"imageDownloadQueue" finish:^(id sender) { + NSLog(@"所有图片下载完成"); +}]; +``` +## 八.缓存控制 + +某个页面有个请求,需要间隔8小时才能发送请求,没到8小时,则展示缓存数据 +在SimpleDAO的初始化方法中添加如下一行 +```objectivec +self.cacheType = [HNCacheTypeAlternative createWtihNextRequstInterval:60*60*8]; +``` +还有其他缓存类型可选`HNCacheTypeBoth`,`HNCacheTypeOnlyWrite`,也可以自定义,需要用到再去研究吧。 +缓存存放位置 = `[HFileCache shareCache].cacheDir` + +再试一下,发现缓存起作用了。 +但是我们发现输入ID不同,没有导致缓存失效,但是,按照这个接口的意义,不同的ID应该对应不同的缓存对象!怎么办呢? +那么我们需要给缓存key添加ID,在SimpleDAO.m中添加如下函数 +```objectivec +- (NSString *)cacheKey +{ + return [[super cacheKey] stringByAppendingString:self.searchKey]; +} +``` +再试试写死ID,或者更改ID的情况 + + +## 九.请求模拟-Mock + +###9.1 自动Mock +自动mock的基础是猜测法,意思是说,框架来猜你要啥样的数据 + +##### 9.1.1 添加一个依赖,并执行pod update + +pod 'HAccessTools' + +##### 9.1.2 在viewDidLoad中添加 +```objectivec +static dispatch_once_t onceToken; +dispatch_once(&onceToken, ^{ + [[HNetworkAutoMock shared] enable]; + [HNetworkAutoMock shared].baseformat = @{@"status":@YES}; +}); +``` +###### 9.1.3 将SimpleDAO设置为mock模式,在init函数中添加 +```objectivec +self.isMock = YES; +``` +运行一下吧 + +运行起来报错了,原因是url不合法,看一下数据,原来是src不对. +我们可以看到数据,同时下发了url和fullImageURL, 我们用一下这个地址发现只有fullImageURL这是一个有效的图片地址 +这是因为,在自动mock中,猜对了fullImageURL的值应该是啥样的,但是”url”的命名框架猜不出来。 + +对于自动mock,命名越准确,注解写的越准确,那么就越容易猜中。 + +我们这里暂时把fullImageURL改成“可选的”,以完成这个案例 +ppx(fullImageURL, HPOptional) +运行一下吧. +##### 9.1.4 指定mock类型 +如果框架猜测的结果不对,那么可以手动指定某一个属性mock成什么数据,例如 +``` +ppx(server_modified, HPMockAsDate) +ppx(server_created, HPMockAsDate) +``` +这几个注解定义在“HNetworkAutoMock.h”中 + +对于多层次的model也是可用的, 在HAccessTools里面有更加清楚的demo +注意: 模拟数据的随机图片地址是可裁剪的,在将要发送图片请求时修改一下url +```objectivec +if ([url containsString:@"unsplash.it"]) +{ + NSRange range = [url rangeOfString:@"image="]; + url = [NSString stringWithFormat:@"https://unsplash.it/%d/%d?%@",(int)w,(int)h, [url substringFromIndex:range.location]]; + return url; +} +``` +占位符是显示区域宽高 + +### 9.2 手动Mock +不开启HNetworkAutoMock的情况下,打开isMock开关,则走手动mock +在项目中建立一个`HNetworkDAO.bundle`, 在里面建立`SimpleDAO同名`的一个json文件即可,在这个json文件里面就是手写json数据了 + +## 十.其他问题 + +a.文件上传见HNetworkMultiDataObj + +b.请合理设计你的接口类继承关系 + +c.HDeserializableObject和HDatabaseDAO的可以直接参考HAccess的demo + +d.HFileCache是一种扔进去就不用管了的缓存,见这个库的demo