diff --git a/modules/swagger-codegen/src/main/java/io/swagger/codegen/languages/ObjcClientCodegen.java b/modules/swagger-codegen/src/main/java/io/swagger/codegen/languages/ObjcClientCodegen.java index 21e319f6ec9..0ecdfb667c9 100644 --- a/modules/swagger-codegen/src/main/java/io/swagger/codegen/languages/ObjcClientCodegen.java +++ b/modules/swagger-codegen/src/main/java/io/swagger/codegen/languages/ObjcClientCodegen.java @@ -223,6 +223,8 @@ public class ObjcClientCodegen extends DefaultCodegen implements CodegenConfig { supportingFiles.add(new SupportingFile("JSONResponseSerializer-body.mustache", swaggerFolder, classPrefix + "JSONResponseSerializer.m")); supportingFiles.add(new SupportingFile("JSONRequestSerializer-body.mustache", swaggerFolder, classPrefix + "JSONRequestSerializer.m")); supportingFiles.add(new SupportingFile("JSONRequestSerializer-header.mustache", swaggerFolder, classPrefix + "JSONRequestSerializer.h")); + supportingFiles.add(new SupportingFile("ResponseDeserializer-body.mustache", swaggerFolder, classPrefix + "ResponseDeserializer.m")); + supportingFiles.add(new SupportingFile("ResponseDeserializer-header.mustache", swaggerFolder, classPrefix + "ResponseDeserializer.h")); supportingFiles.add(new SupportingFile("JSONValueTransformer+ISO8601.m", swaggerFolder, "JSONValueTransformer+ISO8601.m")); supportingFiles.add(new SupportingFile("JSONValueTransformer+ISO8601.h", swaggerFolder, "JSONValueTransformer+ISO8601.h")); supportingFiles.add(new SupportingFile("Configuration-body.mustache", swaggerFolder, classPrefix + "Configuration.m")); diff --git a/modules/swagger-codegen/src/main/resources/objc/ApiClient-body.mustache b/modules/swagger-codegen/src/main/resources/objc/ApiClient-body.mustache index 18b756f78b0..a8644bab030 100644 --- a/modules/swagger-codegen/src/main/resources/objc/ApiClient-body.mustache +++ b/modules/swagger-codegen/src/main/resources/objc/ApiClient-body.mustache @@ -2,10 +2,6 @@ NSString *const {{classPrefix}}ResponseObjectErrorKey = @"{{classPrefix}}ResponseObject"; -NSString *const {{classPrefix}}DeserializationErrorDomainKey = @"{{classPrefix}}DeserializationErrorDomainKey"; - -NSInteger const {{classPrefix}}TypeMismatchErrorCode = 143553; - static long requestId = 0; static bool offlineState = false; static NSMutableSet * queuedRequests = nil; @@ -33,6 +29,7 @@ static void (^reachabilityChangeBlock)(int); self.requestSerializer = [AFJSONRequestSerializer serializer]; self.responseSerializer = [AFJSONResponseSerializer serializer]; self.securityPolicy = [self customSecurityPolicy]; + self.responseDeserializer = [[{{classPrefix}}ResponseDeserializer alloc] init]; // configure reachability [self configureCacheReachibility]; } @@ -290,154 +287,6 @@ static void (^reachabilityChangeBlock)(int); [self.reachabilityManager startMonitoring]; } -#pragma mark - Deserialize methods - -- (id) deserialize:(id) data class:(NSString *) class error:(NSError **) error { - // return nil if data is nil or class is nil - if (!data || !class) { - return nil; - } - - // remove "*" from class, if ends with "*" - if ([class hasSuffix:@"*"]) { - class = [class substringToIndex:[class length] - 1]; - } - - // pure object - if ([class isEqualToString:@"NSObject"]) { - return data; - } - - NSRegularExpression *regexp = nil; - NSTextCheckingResult *match = nil; - NSMutableArray *resultArray = nil; - NSMutableDictionary *resultDict = nil; - NSString *innerType = nil; - - // list of models - NSString *arrayOfModelsPat = @"NSArray<(.+)>"; - regexp = [NSRegularExpression regularExpressionWithPattern:arrayOfModelsPat - options:NSRegularExpressionCaseInsensitive - error:nil]; - - match = [regexp firstMatchInString:class - options:0 - range:NSMakeRange(0, [class length])]; - - if (match) { - if(![data isKindOfClass: [NSArray class]]) { - if(error) { - NSDictionary * userInfo = @{NSLocalizedDescriptionKey : NSLocalizedString(@"Received response is not an array", nil)}; - *error = [NSError errorWithDomain:{{classPrefix}}DeserializationErrorDomainKey code:{{classPrefix}}TypeMismatchErrorCode userInfo:userInfo]; - } - return nil; - } - NSArray *dataArray = data; - innerType = [class substringWithRange:[match rangeAtIndex:1]]; - - resultArray = [NSMutableArray arrayWithCapacity:[dataArray count]]; - [data enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) { - id arrObj = [self deserialize:obj class:innerType error:error]; - if(arrObj) { - [resultArray addObject:arrObj]; - } else { - * stop = YES; - } - }]; - - return resultArray; - } - - // list of primitives - NSString *arrayOfPrimitivesPat = @"NSArray\\* /\\* (.+) \\*/"; - regexp = [NSRegularExpression regularExpressionWithPattern:arrayOfPrimitivesPat - options:NSRegularExpressionCaseInsensitive - error:nil]; - match = [regexp firstMatchInString:class - options:0 - range:NSMakeRange(0, [class length])]; - - if (match) { - NSArray *dataArray = data; - innerType = [class substringWithRange:[match rangeAtIndex:1]]; - - resultArray = [NSMutableArray arrayWithCapacity:[dataArray count]]; - [data enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) { - id arrObj = [self deserialize:obj class:innerType error:error]; - if(arrObj) { - [resultArray addObject:arrObj]; - } else { - * stop = YES; - } - }]; - - return resultArray; - } - - // map - NSString *dictPat = @"NSDictionary\\* /\\* (.+?), (.+) \\*/"; - regexp = [NSRegularExpression regularExpressionWithPattern:dictPat - options:NSRegularExpressionCaseInsensitive - error:nil]; - match = [regexp firstMatchInString:class - options:0 - range:NSMakeRange(0, [class length])]; - - if (match) { - NSDictionary *dataDict = data; - NSString *valueType = [class substringWithRange:[match rangeAtIndex:2]]; - - resultDict = [NSMutableDictionary dictionaryWithCapacity:[dataDict count]]; - [data enumerateKeysAndObjectsUsingBlock:^(id key, id obj, BOOL *stop) { - id dicObj = [self deserialize:obj class:valueType error:error]; - if(dicObj) { - [resultDict setValue:dicObj forKey:key]; - } else { - * stop = YES; - } - }]; - - return resultDict; - } - - // primitives - NSArray *primitiveTypes = @[@"NSString", @"NSDate", @"NSNumber"]; - - if ([primitiveTypes containsObject:class]) { - if ([class isEqualToString:@"NSString"]) { - return [NSString stringWithString:data]; - } - else if ([class isEqualToString:@"NSDate"]) { - return [NSDate dateWithISO8601String:data]; - } - else if ([class isEqualToString:@"NSNumber"]) { - // NSNumber from NSNumber - if ([data isKindOfClass:[NSNumber class]]) { - return data; - } - else if ([data isKindOfClass:[NSString class]]) { - // NSNumber (NSCFBoolean) from NSString - if ([[data lowercaseString] isEqualToString:@"true"] || [[data lowercaseString] isEqualToString:@"false"]) { - return [NSNumber numberWithBool:[data boolValue]]; - // NSNumber from NSString - } else { - NSNumberFormatter *formatter = [[NSNumberFormatter alloc] init]; - formatter.numberStyle = NSNumberFormatterDecimalStyle; - return [formatter numberFromString:data]; - } - } - } - } - - // model - Class ModelClass = NSClassFromString(class); - if ([ModelClass instancesRespondToSelector:@selector(initWithDictionary:error:)]) { - return [(JSONModel *) [ModelClass alloc] initWithDictionary:data error:error]; - } - - return nil; -} - #pragma mark - Operation Methods - (void) operationWithCompletionBlock: (NSURLRequest *)request @@ -661,7 +510,7 @@ static void (^reachabilityChangeBlock)(int); else { [self operationWithCompletionBlock:request requestId:requestId completionBlock:^(id data, NSError *error) { NSError * serializationError; - id response = [self deserialize:data class:responseType error:&serializationError]; + id response = [self.responseDeserializer deserialize:data class:responseType error:&serializationError]; if(!response && !error){ error = serializationError; } diff --git a/modules/swagger-codegen/src/main/resources/objc/ApiClient-header.mustache b/modules/swagger-codegen/src/main/resources/objc/ApiClient-header.mustache index 1cf3db5e523..c7caa7ab11e 100644 --- a/modules/swagger-codegen/src/main/resources/objc/ApiClient-header.mustache +++ b/modules/swagger-codegen/src/main/resources/objc/ApiClient-header.mustache @@ -5,7 +5,7 @@ #import "{{classPrefix}}JSONRequestSerializer.h" #import "{{classPrefix}}QueryParamCollection.h" #import "{{classPrefix}}Configuration.h" - +#import "{{classPrefix}}ResponseDeserializer.h" /** * NOTE: This class is auto generated by the swagger code generator program. * https://github.com/swagger-api/swagger-codegen @@ -25,16 +25,6 @@ */ extern NSString *const {{classPrefix}}ResponseObjectErrorKey; -/** - * A key for deserialization ErrorDomain - */ -extern NSString *const {{classPrefix}}DeserializationErrorDomainKey; - -/** - * Code for deserialization type mismatch error - */ -extern NSInteger const {{classPrefix}}TypeMismatchErrorCode; - /** * Log debug message macro */ @@ -49,6 +39,7 @@ extern NSInteger const {{classPrefix}}TypeMismatchErrorCode; /// In order to ensure the HTTPResponseHeaders are correct, it is recommended to initialize one {{classPrefix}}ApiClient instance per thread. @property(nonatomic, readonly) NSDictionary* HTTPResponseHeaders; +@property(nonatomic, strong) id<{{classPrefix}}ResponseDeserializer> responseDeserializer; /** * Clears Cache */ @@ -176,15 +167,6 @@ extern NSInteger const {{classPrefix}}TypeMismatchErrorCode; queryParams:(NSDictionary **)querys WithAuthSettings:(NSArray *)authSettings; -/** - * Deserializes the given data to Objective-C object. - * - * @param data The data will be deserialized. - * @param class The type of objective-c object. - * @param error The error - */ -- (id) deserialize:(id) data class:(NSString *) class error:(NSError**)error; - /** * Logs request and response * diff --git a/modules/swagger-codegen/src/main/resources/objc/Object-body.mustache b/modules/swagger-codegen/src/main/resources/objc/Object-body.mustache index 122484f1ad4..b4599c34f3f 100644 --- a/modules/swagger-codegen/src/main/resources/objc/Object-body.mustache +++ b/modules/swagger-codegen/src/main/resources/objc/Object-body.mustache @@ -1,4 +1,13 @@ #import "{{classPrefix}}Object.h" @implementation {{classPrefix}}Object + +/** + * Gets the string presentation of the object. + * This method will be called when logging model object using `NSLog`. + */ +- (NSString *)description { + return [[self toDictionary] description]; +} + @end diff --git a/modules/swagger-codegen/src/main/resources/objc/ResponseDeserializer-body.mustache b/modules/swagger-codegen/src/main/resources/objc/ResponseDeserializer-body.mustache new file mode 100644 index 00000000000..27a59e39cdc --- /dev/null +++ b/modules/swagger-codegen/src/main/resources/objc/ResponseDeserializer-body.mustache @@ -0,0 +1,231 @@ +#import "{{classPrefix}}ResponseDeserializer.h" +#import +#import + +NSString *const {{classPrefix}}DeserializationErrorDomainKey = @"{{classPrefix}}DeserializationErrorDomainKey"; + +NSInteger const {{classPrefix}}TypeMismatchErrorCode = 143553; + +NSInteger const {{classPrefix}}EmptyValueOccurredErrorCode = 143509; + +NSInteger const {{classPrefix}}UnknownResponseObjectErrorCode = 143528; + + +@interface {{classPrefix}}ResponseDeserializer () + +@property (nonatomic, strong) NSNumberFormatter* numberFormatter; +@property (nonatomic, strong) NSArray *primitiveTypes; +@property (nonatomic, strong) NSArray *basicReturnTypes; + +@property (nonatomic, strong) NSRegularExpression* arrayOfModelsPatExpression; +@property (nonatomic, strong) NSRegularExpression* arrayOfPrimitivesPatExpression; +@property (nonatomic, strong) NSRegularExpression* dictPatExpression; +@property (nonatomic, strong) NSRegularExpression* dictModelsPatExpression; + +@end + +@implementation {{classPrefix}}ResponseDeserializer + +- (instancetype)init { + self = [super init]; + if (self) { + NSNumberFormatter *formatter = [[NSNumberFormatter alloc] init]; + formatter.numberStyle = NSNumberFormatterDecimalStyle; + _numberFormatter = formatter; + _primitiveTypes = @[@"NSString", @"NSDate", @"NSNumber"]; + _basicReturnTypes = @[@"NSObject", @"id"]; + _arrayOfModelsPatExpression = [NSRegularExpression regularExpressionWithPattern:@"NSArray<(.+)>" + options:NSRegularExpressionCaseInsensitive + error:nil]; + _arrayOfPrimitivesPatExpression = [NSRegularExpression regularExpressionWithPattern:@"NSArray\\* /\\* (.+) \\*/" + options:NSRegularExpressionCaseInsensitive + error:nil]; + _dictPatExpression = [NSRegularExpression regularExpressionWithPattern:@"NSDictionary\\* /\\* (.+?), (.+) \\*/" + options:NSRegularExpressionCaseInsensitive + error:nil]; + _dictModelsPatExpression = [NSRegularExpression regularExpressionWithPattern:@"NSDictionary\\<(.+?), (.+)*\\>" + options:NSRegularExpressionCaseInsensitive + error:nil]; + } + return self; +} + +#pragma mark - Deserialize methods + +- (id) deserialize:(id) data class:(NSString *) className error:(NSError **) error { + // return nil if data is nil or className is nil + if (!data || !className || [data isKindOfClass:[NSNull class]]) { + return nil; + } + + // remove "*" from className, if ends with "*" + if ([className hasSuffix:@"*"]) { + className = [className substringToIndex:[className length] - 1]; + } + // pure object + if ([self.basicReturnTypes containsObject:className]) { + return data; + } + + // primitives + if ([self.primitiveTypes containsObject:className]) { + return [self deserializePrimitiveValue:data class:className error:error]; + } + + NSTextCheckingResult *match = nil; + NSRange range = NSMakeRange(0, [className length]); + // list of models + match = [self.arrayOfModelsPatExpression firstMatchInString:className options:0 range:range]; + if (match) { + NSString *innerType = [className substringWithRange:[match rangeAtIndex:1]]; + return [self deserializeArrayValue:data innerType:innerType error:error]; + } + + // list of primitives + match = [self.arrayOfPrimitivesPatExpression firstMatchInString:className options:0 range:range]; + if (match) { + NSString *innerType = [className substringWithRange:[match rangeAtIndex:1]]; + return [self deserializeArrayValue:data innerType:innerType error:error]; + } + + // map + match = [self.dictPatExpression firstMatchInString:className options:0 range:range]; + if (match) { + NSString *valueType = [className substringWithRange:[match rangeAtIndex:2]]; + return [self deserializeDictionaryValue:data valueType:valueType error:error]; + } + + match = [self.dictModelsPatExpression firstMatchInString:className options:0 range:range]; + if (match) { + NSString *valueType = [className substringWithRange:[match rangeAtIndex:2]]; + return [self deserializeDictionaryValue:data valueType:valueType error:error]; + } + + // model + Class ModelClass = NSClassFromString(className); + if ([ModelClass instancesRespondToSelector:@selector(initWithDictionary:error:)]) { + return [(JSONModel *) [ModelClass alloc] initWithDictionary:data error:error]; + } + + if(error) { + *error = [self unknownResponseErrorWithExpectedType:className data:data]; + } + return nil; +} + +- (id) deserializeDictionaryValue:(id) data valueType:(NSString *) valueType error:(NSError**)error { + if(![data isKindOfClass: [NSDictionary class]]) { + if(error) { + *error = [self typeMismatchErrorWithExpectedType:NSStringFromClass([NSDictionary class]) data:data]; + } + return nil; + } + __block NSMutableDictionary *resultDict = [NSMutableDictionary dictionaryWithCapacity:[data count]]; + for (id key in [data allKeys]) { + id obj = [data valueForKey:key]; + id dicObj = [self deserialize:obj class:valueType error:error]; + if(dicObj) { + [resultDict setValue:dicObj forKey:key]; + } else if([obj isKindOfClass:[NSNull class]]) { + if(self.treatNullAsError) { + if (error) { + *error = [self emptyValueOccurredError]; + } + resultDict = nil; + break; + } + } else { + resultDict = nil; + break; + } + } + return resultDict; +} + +- (id) deserializeArrayValue:(id) data innerType:(NSString *) innerType error:(NSError**)error { + if(![data isKindOfClass: [NSArray class]]) { + if(error) { + *error = [self typeMismatchErrorWithExpectedType:NSStringFromClass([NSArray class]) data:data]; + } + return nil; + } + NSMutableArray* resultArray = [NSMutableArray arrayWithCapacity:[data count]]; + for (id obj in data) { + id arrObj = [self deserialize:obj class:innerType error:error]; + if(arrObj) { + [resultArray addObject:arrObj]; + } else if([obj isKindOfClass:[NSNull class]]) { + if(self.treatNullAsError) { + if (error) { + *error = [self emptyValueOccurredError]; + } + resultArray = nil; + break; + } + } else { + resultArray = nil; + break; + } + } + return resultArray; +}; + +- (id) deserializePrimitiveValue:(id) data class:(NSString *) className error:(NSError**)error { + if ([className isEqualToString:@"NSString"]) { + return [NSString stringWithString:data]; + } + else if ([className isEqualToString:@"NSDate"]) { + return [self deserializeDateValue:data error:error]; + } + else if ([className isEqualToString:@"NSNumber"]) { + // NSNumber from NSNumber + if ([data isKindOfClass:[NSNumber class]]) { + return data; + } + else if ([data isKindOfClass:[NSString class]]) { + // NSNumber (NSCFBoolean) from NSString + if ([[data lowercaseString] isEqualToString:@"true"] || [[data lowercaseString] isEqualToString:@"false"]) { + return @([data boolValue]); + // NSNumber from NSString + } else { + NSNumber* formattedValue = [self.numberFormatter numberFromString:data]; + if(!formattedValue && [data length] > 0 && error) { + *error = [self typeMismatchErrorWithExpectedType:className data:data]; + } + return formattedValue; + } + } + } + if(error) { + *error = [self typeMismatchErrorWithExpectedType:className data:data]; + } + return nil; +} + +- (id) deserializeDateValue:(id) data error:(NSError**)error { + NSDate *date =[NSDate dateWithISO8601String:data]; + if(!date && [data length] > 0 && error) { + *error = [self typeMismatchErrorWithExpectedType:NSStringFromClass([NSDate class]) data:data]; + } + return date; +}; + +-(NSError *)typeMismatchErrorWithExpectedType:(NSString *)expected data:(id)data { + NSString * message = [NSString stringWithFormat:NSLocalizedString(@"Received response [%@] is not an object of type %@",nil),data, expected]; + NSDictionary * userInfo = @{NSLocalizedDescriptionKey : message}; + return [NSError errorWithDomain:{{classPrefix}}DeserializationErrorDomainKey code:{{classPrefix}}TypeMismatchErrorCode userInfo:userInfo]; +} + +-(NSError *)emptyValueOccurredError { + NSString * message = NSLocalizedString(@"Received response contains null value in dictionary or array response",nil); + NSDictionary * userInfo = @{NSLocalizedDescriptionKey : message}; + return [NSError errorWithDomain:{{classPrefix}}DeserializationErrorDomainKey code:{{classPrefix}}EmptyValueOccurredErrorCode userInfo:userInfo]; +} + +-(NSError *)unknownResponseErrorWithExpectedType:(NSString *)expected data:(id)data { + NSString * message = [NSString stringWithFormat:NSLocalizedString(@"Unknown response expected type %@ [reponse: %@]",nil),expected,data]; + NSDictionary * userInfo = @{NSLocalizedDescriptionKey : message}; + return [NSError errorWithDomain:{{classPrefix}}DeserializationErrorDomainKey code:{{classPrefix}}UnknownResponseObjectErrorCode userInfo:userInfo]; +} + +@end diff --git a/modules/swagger-codegen/src/main/resources/objc/ResponseDeserializer-header.mustache b/modules/swagger-codegen/src/main/resources/objc/ResponseDeserializer-header.mustache new file mode 100644 index 00000000000..2dd038c13f0 --- /dev/null +++ b/modules/swagger-codegen/src/main/resources/objc/ResponseDeserializer-header.mustache @@ -0,0 +1,50 @@ +#import + +/** + * NOTE: This class is auto generated by the swagger code generator program. + * https://github.com/swagger-api/swagger-codegen + * Do not edit the class manually. + */ + +/** + * A key for deserialization ErrorDomain + */ +extern NSString *const {{classPrefix}}DeserializationErrorDomainKey; + +/** + * Code for deserialization type mismatch error + */ +extern NSInteger const {{classPrefix}}TypeMismatchErrorCode; + +/** + * Code for deserialization empty value error + */ +extern NSInteger const {{classPrefix}}EmptyValueOccurredErrorCode; + +/** + * Error code for unknown response + */ +extern NSInteger const {{classPrefix}}UnknownResponseObjectErrorCode; + +@protocol {{classPrefix}}ResponseDeserializer + +/** + * Deserializes the given data to Objective-C object. + * + * @param data The data will be deserialized. + * @param class The type of objective-c object. + * @param error The error + */ +- (id) deserialize:(id) data class:(NSString *) className error:(NSError**)error; + +@end + +@interface {{classPrefix}}ResponseDeserializer : NSObject <{{classPrefix}}ResponseDeserializer> + +/** + * If an null value occurs in dictionary or array if set to YES whole response will be invalid else will be ignored + * @default NO + */ +@property (nonatomic, assign) BOOL treatNullAsError; + +@end diff --git a/modules/swagger-codegen/src/main/resources/objc/model-body.mustache b/modules/swagger-codegen/src/main/resources/objc/model-body.mustache index ec5ab3b9f80..776cd55ccef 100644 --- a/modules/swagger-codegen/src/main/resources/objc/model-body.mustache +++ b/modules/swagger-codegen/src/main/resources/objc/model-body.mustache @@ -6,13 +6,11 @@ - (instancetype)init { self = [super init]; - if (self) { // initalise property's default value, if any {{#vars}}{{#defaultValue}}self.{{name}} = {{{defaultValue}}}; {{/defaultValue}}{{/vars}} } - return self; } @@ -22,7 +20,6 @@ */ - (id)initWithDictionary:(NSDictionary *)dict error:(NSError *__autoreleasing *)err { - NSString * discriminatedClassName = [dict valueForKey:@"{{discriminator}}"]; if(discriminatedClassName == nil ){ @@ -45,8 +42,7 @@ * Maps json key to property name. * This method is used by `JSONModel`. */ -+ (JSONKeyMapper *)keyMapper -{ ++ (JSONKeyMapper *)keyMapper { return [[JSONKeyMapper alloc] initWithDictionary:@{ {{#vars}}@"{{baseName}}": @"{{name}}"{{#hasMore}}, {{/hasMore}}{{/vars}} }]; } @@ -55,24 +51,11 @@ * If `propertyName` is optional, then return `YES`, otherwise return `NO`. * This method is used by `JSONModel`. */ -+ (BOOL)propertyIsOptional:(NSString *)propertyName -{ ++ (BOOL)propertyIsOptional:(NSString *)propertyName { + NSArray *optionalProperties = @[{{#vars}}{{^required}}@"{{name}}"{{#hasMore}}, {{/hasMore}}{{/required}}{{/vars}}]; + return [optionalProperties containsObject:propertyName]; - if ([optionalProperties containsObject:propertyName]) { - return YES; - } - else { - return NO; - } -} - -/** - * Gets the string presentation of the object. - * This method will be called when logging model object using `NSLog`. - */ -- (NSString *)description { - return [[self toDictionary] description]; } {{/model}} diff --git a/samples/client/petstore/objc/README.md b/samples/client/petstore/objc/README.md index bd0f24851a7..a3550545c24 100644 --- a/samples/client/petstore/objc/README.md +++ b/samples/client/petstore/objc/README.md @@ -6,7 +6,7 @@ This ObjC package is automatically generated by the [Swagger Codegen](https://gi - API version: 1.0.0 - Package version: -- Build date: 2016-04-25T18:52:19.055+02:00 +- Build date: 2016-05-06T12:20:47.112+02:00 - Build package: class io.swagger.codegen.languages.ObjcClientCodegen ## Requirements @@ -19,7 +19,7 @@ The SDK requires [**ARC (Automatic Reference Counting)**](http://stackoverflow.c Add the following to the Podfile: ```ruby -pod 'SwaggerClient', :git => 'https://github.com/YOUR_GIT_USR_ID/YOUR_GIT_REPO_ID.git' +pod 'SwaggerClient', :git => 'https://github.com/GIT_USER_ID/GIT_REPO_ID.git' ``` To specify a particular branch, append `, :branch => 'branch-name-here'` diff --git a/samples/client/petstore/objc/SwaggerClient/SWG200Response.h b/samples/client/petstore/objc/SwaggerClient/SWG200Response.h deleted file mode 100644 index c576bc204ad..00000000000 --- a/samples/client/petstore/objc/SwaggerClient/SWG200Response.h +++ /dev/null @@ -1,20 +0,0 @@ -#import -#import "SWGObject.h" - -/** - * NOTE: This class is auto generated by the swagger code generator program. - * https://github.com/swagger-api/swagger-codegen - * Do not edit the class manually. - */ - - - -@protocol SWG200Response -@end - -@interface SWG200Response : SWGObject - - -@property(nonatomic) NSNumber* name; - -@end diff --git a/samples/client/petstore/objc/SwaggerClient/SWG200Response.m b/samples/client/petstore/objc/SwaggerClient/SWG200Response.m deleted file mode 100644 index 9cafc782f1a..00000000000 --- a/samples/client/petstore/objc/SwaggerClient/SWG200Response.m +++ /dev/null @@ -1,51 +0,0 @@ -#import "SWG200Response.h" - -@implementation SWG200Response - -- (instancetype)init { - self = [super init]; - - if (self) { - // initalise property's default value, if any - - } - - return self; -} - - -/** - * Maps json key to property name. - * This method is used by `JSONModel`. - */ -+ (JSONKeyMapper *)keyMapper -{ - return [[JSONKeyMapper alloc] initWithDictionary:@{ @"name": @"name" }]; -} - -/** - * Indicates whether the property with the given name is optional. - * If `propertyName` is optional, then return `YES`, otherwise return `NO`. - * This method is used by `JSONModel`. - */ -+ (BOOL)propertyIsOptional:(NSString *)propertyName -{ - NSArray *optionalProperties = @[@"name"]; - - if ([optionalProperties containsObject:propertyName]) { - return YES; - } - else { - return NO; - } -} - -/** - * Gets the string presentation of the object. - * This method will be called when logging model object using `NSLog`. - */ -- (NSString *)description { - return [[self toDictionary] description]; -} - -@end diff --git a/samples/client/petstore/objc/SwaggerClient/SWGApiClient.h b/samples/client/petstore/objc/SwaggerClient/SWGApiClient.h index 5d40ef2f5f0..7dea87a3327 100644 --- a/samples/client/petstore/objc/SwaggerClient/SWGApiClient.h +++ b/samples/client/petstore/objc/SwaggerClient/SWGApiClient.h @@ -5,7 +5,7 @@ #import "SWGJSONRequestSerializer.h" #import "SWGQueryParamCollection.h" #import "SWGConfiguration.h" - +#import "SWGResponseDeserializer.h" /** * NOTE: This class is auto generated by the swagger code generator program. * https://github.com/swagger-api/swagger-codegen @@ -29,16 +29,6 @@ */ extern NSString *const SWGResponseObjectErrorKey; -/** - * A key for deserialization ErrorDomain - */ -extern NSString *const SWGDeserializationErrorDomainKey; - -/** - * Code for deserialization type mismatch error - */ -extern NSInteger const SWGTypeMismatchErrorCode; - /** * Log debug message macro */ @@ -53,6 +43,7 @@ extern NSInteger const SWGTypeMismatchErrorCode; /// In order to ensure the HTTPResponseHeaders are correct, it is recommended to initialize one SWGApiClient instance per thread. @property(nonatomic, readonly) NSDictionary* HTTPResponseHeaders; +@property(nonatomic, strong) id responseDeserializer; /** * Clears Cache */ @@ -180,15 +171,6 @@ extern NSInteger const SWGTypeMismatchErrorCode; queryParams:(NSDictionary **)querys WithAuthSettings:(NSArray *)authSettings; -/** - * Deserializes the given data to Objective-C object. - * - * @param data The data will be deserialized. - * @param class The type of objective-c object. - * @param error The error - */ -- (id) deserialize:(id) data class:(NSString *) class error:(NSError**)error; - /** * Logs request and response * diff --git a/samples/client/petstore/objc/SwaggerClient/SWGApiClient.m b/samples/client/petstore/objc/SwaggerClient/SWGApiClient.m index ecdf4aaafb2..5d5d2019b0f 100644 --- a/samples/client/petstore/objc/SwaggerClient/SWGApiClient.m +++ b/samples/client/petstore/objc/SwaggerClient/SWGApiClient.m @@ -2,10 +2,6 @@ NSString *const SWGResponseObjectErrorKey = @"SWGResponseObject"; -NSString *const SWGDeserializationErrorDomainKey = @"SWGDeserializationErrorDomainKey"; - -NSInteger const SWGTypeMismatchErrorCode = 143553; - static long requestId = 0; static bool offlineState = false; static NSMutableSet * queuedRequests = nil; @@ -33,6 +29,7 @@ static void (^reachabilityChangeBlock)(int); self.requestSerializer = [AFJSONRequestSerializer serializer]; self.responseSerializer = [AFJSONResponseSerializer serializer]; self.securityPolicy = [self customSecurityPolicy]; + self.responseDeserializer = [[SWGResponseDeserializer alloc] init]; // configure reachability [self configureCacheReachibility]; } @@ -290,154 +287,6 @@ static void (^reachabilityChangeBlock)(int); [self.reachabilityManager startMonitoring]; } -#pragma mark - Deserialize methods - -- (id) deserialize:(id) data class:(NSString *) class error:(NSError **) error { - // return nil if data is nil or class is nil - if (!data || !class) { - return nil; - } - - // remove "*" from class, if ends with "*" - if ([class hasSuffix:@"*"]) { - class = [class substringToIndex:[class length] - 1]; - } - - // pure object - if ([class isEqualToString:@"NSObject"]) { - return data; - } - - NSRegularExpression *regexp = nil; - NSTextCheckingResult *match = nil; - NSMutableArray *resultArray = nil; - NSMutableDictionary *resultDict = nil; - NSString *innerType = nil; - - // list of models - NSString *arrayOfModelsPat = @"NSArray<(.+)>"; - regexp = [NSRegularExpression regularExpressionWithPattern:arrayOfModelsPat - options:NSRegularExpressionCaseInsensitive - error:nil]; - - match = [regexp firstMatchInString:class - options:0 - range:NSMakeRange(0, [class length])]; - - if (match) { - if(![data isKindOfClass: [NSArray class]]) { - if(error) { - NSDictionary * userInfo = @{NSLocalizedDescriptionKey : NSLocalizedString(@"Received response is not an array", nil)}; - *error = [NSError errorWithDomain:SWGDeserializationErrorDomainKey code:SWGTypeMismatchErrorCode userInfo:userInfo]; - } - return nil; - } - NSArray *dataArray = data; - innerType = [class substringWithRange:[match rangeAtIndex:1]]; - - resultArray = [NSMutableArray arrayWithCapacity:[dataArray count]]; - [data enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) { - id arrObj = [self deserialize:obj class:innerType error:error]; - if(arrObj) { - [resultArray addObject:arrObj]; - } else { - * stop = YES; - } - }]; - - return resultArray; - } - - // list of primitives - NSString *arrayOfPrimitivesPat = @"NSArray\\* /\\* (.+) \\*/"; - regexp = [NSRegularExpression regularExpressionWithPattern:arrayOfPrimitivesPat - options:NSRegularExpressionCaseInsensitive - error:nil]; - match = [regexp firstMatchInString:class - options:0 - range:NSMakeRange(0, [class length])]; - - if (match) { - NSArray *dataArray = data; - innerType = [class substringWithRange:[match rangeAtIndex:1]]; - - resultArray = [NSMutableArray arrayWithCapacity:[dataArray count]]; - [data enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) { - id arrObj = [self deserialize:obj class:innerType error:error]; - if(arrObj) { - [resultArray addObject:arrObj]; - } else { - * stop = YES; - } - }]; - - return resultArray; - } - - // map - NSString *dictPat = @"NSDictionary\\* /\\* (.+?), (.+) \\*/"; - regexp = [NSRegularExpression regularExpressionWithPattern:dictPat - options:NSRegularExpressionCaseInsensitive - error:nil]; - match = [regexp firstMatchInString:class - options:0 - range:NSMakeRange(0, [class length])]; - - if (match) { - NSDictionary *dataDict = data; - NSString *valueType = [class substringWithRange:[match rangeAtIndex:2]]; - - resultDict = [NSMutableDictionary dictionaryWithCapacity:[dataDict count]]; - [data enumerateKeysAndObjectsUsingBlock:^(id key, id obj, BOOL *stop) { - id dicObj = [self deserialize:obj class:valueType error:error]; - if(dicObj) { - [resultDict setValue:dicObj forKey:key]; - } else { - * stop = YES; - } - }]; - - return resultDict; - } - - // primitives - NSArray *primitiveTypes = @[@"NSString", @"NSDate", @"NSNumber"]; - - if ([primitiveTypes containsObject:class]) { - if ([class isEqualToString:@"NSString"]) { - return [NSString stringWithString:data]; - } - else if ([class isEqualToString:@"NSDate"]) { - return [NSDate dateWithISO8601String:data]; - } - else if ([class isEqualToString:@"NSNumber"]) { - // NSNumber from NSNumber - if ([data isKindOfClass:[NSNumber class]]) { - return data; - } - else if ([data isKindOfClass:[NSString class]]) { - // NSNumber (NSCFBoolean) from NSString - if ([[data lowercaseString] isEqualToString:@"true"] || [[data lowercaseString] isEqualToString:@"false"]) { - return [NSNumber numberWithBool:[data boolValue]]; - // NSNumber from NSString - } else { - NSNumberFormatter *formatter = [[NSNumberFormatter alloc] init]; - formatter.numberStyle = NSNumberFormatterDecimalStyle; - return [formatter numberFromString:data]; - } - } - } - } - - // model - Class ModelClass = NSClassFromString(class); - if ([ModelClass instancesRespondToSelector:@selector(initWithDictionary:error:)]) { - return [(JSONModel *) [ModelClass alloc] initWithDictionary:data error:error]; - } - - return nil; -} - #pragma mark - Operation Methods - (void) operationWithCompletionBlock: (NSURLRequest *)request @@ -661,7 +510,7 @@ static void (^reachabilityChangeBlock)(int); else { [self operationWithCompletionBlock:request requestId:requestId completionBlock:^(id data, NSError *error) { NSError * serializationError; - id response = [self deserialize:data class:responseType error:&serializationError]; + id response = [self.responseDeserializer deserialize:data class:responseType error:&serializationError]; if(!response && !error){ error = serializationError; } diff --git a/samples/client/petstore/objc/SwaggerClient/SWGCategory.m b/samples/client/petstore/objc/SwaggerClient/SWGCategory.m index c323eda5a0a..29e3bc16226 100644 --- a/samples/client/petstore/objc/SwaggerClient/SWGCategory.m +++ b/samples/client/petstore/objc/SwaggerClient/SWGCategory.m @@ -4,12 +4,10 @@ - (instancetype)init { self = [super init]; - if (self) { // initalise property's default value, if any } - return self; } @@ -18,8 +16,7 @@ * Maps json key to property name. * This method is used by `JSONModel`. */ -+ (JSONKeyMapper *)keyMapper -{ ++ (JSONKeyMapper *)keyMapper { return [[JSONKeyMapper alloc] initWithDictionary:@{ @"id": @"_id", @"name": @"name" }]; } @@ -28,24 +25,11 @@ * If `propertyName` is optional, then return `YES`, otherwise return `NO`. * This method is used by `JSONModel`. */ -+ (BOOL)propertyIsOptional:(NSString *)propertyName -{ ++ (BOOL)propertyIsOptional:(NSString *)propertyName { + NSArray *optionalProperties = @[@"_id", @"name"]; + return [optionalProperties containsObject:propertyName]; - if ([optionalProperties containsObject:propertyName]) { - return YES; - } - else { - return NO; - } -} - -/** - * Gets the string presentation of the object. - * This method will be called when logging model object using `NSLog`. - */ -- (NSString *)description { - return [[self toDictionary] description]; } @end diff --git a/samples/client/petstore/objc/SwaggerClient/SWGInlineResponse200.h b/samples/client/petstore/objc/SwaggerClient/SWGInlineResponse200.h deleted file mode 100644 index 577434faae6..00000000000 --- a/samples/client/petstore/objc/SwaggerClient/SWGInlineResponse200.h +++ /dev/null @@ -1,32 +0,0 @@ -#import -#import "SWGObject.h" - -/** - * NOTE: This class is auto generated by the swagger code generator program. - * https://github.com/swagger-api/swagger-codegen - * Do not edit the class manually. - */ - -#import "SWGTag.h" - - -@protocol SWGInlineResponse200 -@end - -@interface SWGInlineResponse200 : SWGObject - - -@property(nonatomic) NSArray* /* NSString */ photoUrls; - -@property(nonatomic) NSString* name; - -@property(nonatomic) NSNumber* _id; - -@property(nonatomic) NSObject* category; - -@property(nonatomic) NSArray* tags; -/* pet status in the store [optional] - */ -@property(nonatomic) NSString* status; - -@end diff --git a/samples/client/petstore/objc/SwaggerClient/SWGInlineResponse200.m b/samples/client/petstore/objc/SwaggerClient/SWGInlineResponse200.m deleted file mode 100644 index 5fbe9ec6ec2..00000000000 --- a/samples/client/petstore/objc/SwaggerClient/SWGInlineResponse200.m +++ /dev/null @@ -1,51 +0,0 @@ -#import "SWGInlineResponse200.h" - -@implementation SWGInlineResponse200 - -- (instancetype)init { - self = [super init]; - - if (self) { - // initalise property's default value, if any - - } - - return self; -} - - -/** - * Maps json key to property name. - * This method is used by `JSONModel`. - */ -+ (JSONKeyMapper *)keyMapper -{ - return [[JSONKeyMapper alloc] initWithDictionary:@{ @"photoUrls": @"photoUrls", @"name": @"name", @"id": @"_id", @"category": @"category", @"tags": @"tags", @"status": @"status" }]; -} - -/** - * Indicates whether the property with the given name is optional. - * If `propertyName` is optional, then return `YES`, otherwise return `NO`. - * This method is used by `JSONModel`. - */ -+ (BOOL)propertyIsOptional:(NSString *)propertyName -{ - NSArray *optionalProperties = @[@"photoUrls", @"name", @"category", @"tags", @"status"]; - - if ([optionalProperties containsObject:propertyName]) { - return YES; - } - else { - return NO; - } -} - -/** - * Gets the string presentation of the object. - * This method will be called when logging model object using `NSLog`. - */ -- (NSString *)description { - return [[self toDictionary] description]; -} - -@end diff --git a/samples/client/petstore/objc/SwaggerClient/SWGName.h b/samples/client/petstore/objc/SwaggerClient/SWGName.h deleted file mode 100644 index fd34af7b66a..00000000000 --- a/samples/client/petstore/objc/SwaggerClient/SWGName.h +++ /dev/null @@ -1,22 +0,0 @@ -#import -#import "SWGObject.h" - -/** - * NOTE: This class is auto generated by the swagger code generator program. - * https://github.com/swagger-api/swagger-codegen - * Do not edit the class manually. - */ - - - -@protocol SWGName -@end - -@interface SWGName : SWGObject - - -@property(nonatomic) NSNumber* name; - -@property(nonatomic) NSNumber* snakeCase; - -@end diff --git a/samples/client/petstore/objc/SwaggerClient/SWGName.m b/samples/client/petstore/objc/SwaggerClient/SWGName.m deleted file mode 100644 index 16aec784549..00000000000 --- a/samples/client/petstore/objc/SwaggerClient/SWGName.m +++ /dev/null @@ -1,51 +0,0 @@ -#import "SWGName.h" - -@implementation SWGName - -- (instancetype)init { - self = [super init]; - - if (self) { - // initalise property's default value, if any - - } - - return self; -} - - -/** - * Maps json key to property name. - * This method is used by `JSONModel`. - */ -+ (JSONKeyMapper *)keyMapper -{ - return [[JSONKeyMapper alloc] initWithDictionary:@{ @"name": @"name", @"snake_case": @"snakeCase" }]; -} - -/** - * Indicates whether the property with the given name is optional. - * If `propertyName` is optional, then return `YES`, otherwise return `NO`. - * This method is used by `JSONModel`. - */ -+ (BOOL)propertyIsOptional:(NSString *)propertyName -{ - NSArray *optionalProperties = @[@"snakeCase"]; - - if ([optionalProperties containsObject:propertyName]) { - return YES; - } - else { - return NO; - } -} - -/** - * Gets the string presentation of the object. - * This method will be called when logging model object using `NSLog`. - */ -- (NSString *)description { - return [[self toDictionary] description]; -} - -@end diff --git a/samples/client/petstore/objc/SwaggerClient/SWGObject.m b/samples/client/petstore/objc/SwaggerClient/SWGObject.m index 1f897ab1d56..8085c404f7e 100644 --- a/samples/client/petstore/objc/SwaggerClient/SWGObject.m +++ b/samples/client/petstore/objc/SwaggerClient/SWGObject.m @@ -1,4 +1,13 @@ #import "SWGObject.h" @implementation SWGObject + +/** + * Gets the string presentation of the object. + * This method will be called when logging model object using `NSLog`. + */ +- (NSString *)description { + return [[self toDictionary] description]; +} + @end diff --git a/samples/client/petstore/objc/SwaggerClient/SWGOrder.m b/samples/client/petstore/objc/SwaggerClient/SWGOrder.m index e41bc7a188d..d10ed802b8d 100644 --- a/samples/client/petstore/objc/SwaggerClient/SWGOrder.m +++ b/samples/client/petstore/objc/SwaggerClient/SWGOrder.m @@ -4,12 +4,10 @@ - (instancetype)init { self = [super init]; - if (self) { // initalise property's default value, if any } - return self; } @@ -18,8 +16,7 @@ * Maps json key to property name. * This method is used by `JSONModel`. */ -+ (JSONKeyMapper *)keyMapper -{ ++ (JSONKeyMapper *)keyMapper { return [[JSONKeyMapper alloc] initWithDictionary:@{ @"id": @"_id", @"petId": @"petId", @"quantity": @"quantity", @"shipDate": @"shipDate", @"status": @"status", @"complete": @"complete" }]; } @@ -28,24 +25,11 @@ * If `propertyName` is optional, then return `YES`, otherwise return `NO`. * This method is used by `JSONModel`. */ -+ (BOOL)propertyIsOptional:(NSString *)propertyName -{ ++ (BOOL)propertyIsOptional:(NSString *)propertyName { + NSArray *optionalProperties = @[@"_id", @"petId", @"quantity", @"shipDate", @"status", @"complete"]; + return [optionalProperties containsObject:propertyName]; - if ([optionalProperties containsObject:propertyName]) { - return YES; - } - else { - return NO; - } -} - -/** - * Gets the string presentation of the object. - * This method will be called when logging model object using `NSLog`. - */ -- (NSString *)description { - return [[self toDictionary] description]; } @end diff --git a/samples/client/petstore/objc/SwaggerClient/SWGPet.m b/samples/client/petstore/objc/SwaggerClient/SWGPet.m index 86389c2590b..98e75189ed3 100644 --- a/samples/client/petstore/objc/SwaggerClient/SWGPet.m +++ b/samples/client/petstore/objc/SwaggerClient/SWGPet.m @@ -4,12 +4,10 @@ - (instancetype)init { self = [super init]; - if (self) { // initalise property's default value, if any } - return self; } @@ -18,8 +16,7 @@ * Maps json key to property name. * This method is used by `JSONModel`. */ -+ (JSONKeyMapper *)keyMapper -{ ++ (JSONKeyMapper *)keyMapper { return [[JSONKeyMapper alloc] initWithDictionary:@{ @"id": @"_id", @"category": @"category", @"name": @"name", @"photoUrls": @"photoUrls", @"tags": @"tags", @"status": @"status" }]; } @@ -28,24 +25,11 @@ * If `propertyName` is optional, then return `YES`, otherwise return `NO`. * This method is used by `JSONModel`. */ -+ (BOOL)propertyIsOptional:(NSString *)propertyName -{ ++ (BOOL)propertyIsOptional:(NSString *)propertyName { + NSArray *optionalProperties = @[@"_id", @"category", @"tags", @"status"]; + return [optionalProperties containsObject:propertyName]; - if ([optionalProperties containsObject:propertyName]) { - return YES; - } - else { - return NO; - } -} - -/** - * Gets the string presentation of the object. - * This method will be called when logging model object using `NSLog`. - */ -- (NSString *)description { - return [[self toDictionary] description]; } @end diff --git a/samples/client/petstore/objc/SwaggerClient/SWGResponseDeserializer.h b/samples/client/petstore/objc/SwaggerClient/SWGResponseDeserializer.h new file mode 100644 index 00000000000..b20ead44f83 --- /dev/null +++ b/samples/client/petstore/objc/SwaggerClient/SWGResponseDeserializer.h @@ -0,0 +1,50 @@ +#import + +/** + * NOTE: This class is auto generated by the swagger code generator program. + * https://github.com/swagger-api/swagger-codegen + * Do not edit the class manually. + */ + +/** + * A key for deserialization ErrorDomain + */ +extern NSString *const SWGDeserializationErrorDomainKey; + +/** + * Code for deserialization type mismatch error + */ +extern NSInteger const SWGTypeMismatchErrorCode; + +/** + * Code for deserialization empty value error + */ +extern NSInteger const SWGEmptyValueOccurredErrorCode; + +/** + * Error code for unknown response + */ +extern NSInteger const SWGUnknownResponseObjectErrorCode; + +@protocol SWGResponseDeserializer + +/** + * Deserializes the given data to Objective-C object. + * + * @param data The data will be deserialized. + * @param class The type of objective-c object. + * @param error The error + */ +- (id) deserialize:(id) data class:(NSString *) className error:(NSError**)error; + +@end + +@interface SWGResponseDeserializer : NSObject + +/** + * If an null value occurs in dictionary or array if set to YES whole response will be invalid else will be ignored + * @default NO + */ +@property (nonatomic, assign) BOOL treatNullAsError; + +@end diff --git a/samples/client/petstore/objc/SwaggerClient/SWGResponseDeserializer.m b/samples/client/petstore/objc/SwaggerClient/SWGResponseDeserializer.m new file mode 100644 index 00000000000..fa5be3af3d2 --- /dev/null +++ b/samples/client/petstore/objc/SwaggerClient/SWGResponseDeserializer.m @@ -0,0 +1,231 @@ +#import "SWGResponseDeserializer.h" +#import +#import + +NSString *const SWGDeserializationErrorDomainKey = @"SWGDeserializationErrorDomainKey"; + +NSInteger const SWGTypeMismatchErrorCode = 143553; + +NSInteger const SWGEmptyValueOccurredErrorCode = 143509; + +NSInteger const SWGUnknownResponseObjectErrorCode = 143528; + + +@interface SWGResponseDeserializer () + +@property (nonatomic, strong) NSNumberFormatter* numberFormatter; +@property (nonatomic, strong) NSArray *primitiveTypes; +@property (nonatomic, strong) NSArray *basicReturnTypes; + +@property (nonatomic, strong) NSRegularExpression* arrayOfModelsPatExpression; +@property (nonatomic, strong) NSRegularExpression* arrayOfPrimitivesPatExpression; +@property (nonatomic, strong) NSRegularExpression* dictPatExpression; +@property (nonatomic, strong) NSRegularExpression* dictModelsPatExpression; + +@end + +@implementation SWGResponseDeserializer + +- (instancetype)init { + self = [super init]; + if (self) { + NSNumberFormatter *formatter = [[NSNumberFormatter alloc] init]; + formatter.numberStyle = NSNumberFormatterDecimalStyle; + _numberFormatter = formatter; + _primitiveTypes = @[@"NSString", @"NSDate", @"NSNumber"]; + _basicReturnTypes = @[@"NSObject", @"id"]; + _arrayOfModelsPatExpression = [NSRegularExpression regularExpressionWithPattern:@"NSArray<(.+)>" + options:NSRegularExpressionCaseInsensitive + error:nil]; + _arrayOfPrimitivesPatExpression = [NSRegularExpression regularExpressionWithPattern:@"NSArray\\* /\\* (.+) \\*/" + options:NSRegularExpressionCaseInsensitive + error:nil]; + _dictPatExpression = [NSRegularExpression regularExpressionWithPattern:@"NSDictionary\\* /\\* (.+?), (.+) \\*/" + options:NSRegularExpressionCaseInsensitive + error:nil]; + _dictModelsPatExpression = [NSRegularExpression regularExpressionWithPattern:@"NSDictionary\\<(.+?), (.+)*\\>" + options:NSRegularExpressionCaseInsensitive + error:nil]; + } + return self; +} + +#pragma mark - Deserialize methods + +- (id) deserialize:(id) data class:(NSString *) className error:(NSError **) error { + // return nil if data is nil or className is nil + if (!data || !className || [data isKindOfClass:[NSNull class]]) { + return nil; + } + + // remove "*" from className, if ends with "*" + if ([className hasSuffix:@"*"]) { + className = [className substringToIndex:[className length] - 1]; + } + // pure object + if ([self.basicReturnTypes containsObject:className]) { + return data; + } + + // primitives + if ([self.primitiveTypes containsObject:className]) { + return [self deserializePrimitiveValue:data class:className error:error]; + } + + NSTextCheckingResult *match = nil; + NSRange range = NSMakeRange(0, [className length]); + // list of models + match = [self.arrayOfModelsPatExpression firstMatchInString:className options:0 range:range]; + if (match) { + NSString *innerType = [className substringWithRange:[match rangeAtIndex:1]]; + return [self deserializeArrayValue:data innerType:innerType error:error]; + } + + // list of primitives + match = [self.arrayOfPrimitivesPatExpression firstMatchInString:className options:0 range:range]; + if (match) { + NSString *innerType = [className substringWithRange:[match rangeAtIndex:1]]; + return [self deserializeArrayValue:data innerType:innerType error:error]; + } + + // map + match = [self.dictPatExpression firstMatchInString:className options:0 range:range]; + if (match) { + NSString *valueType = [className substringWithRange:[match rangeAtIndex:2]]; + return [self deserializeDictionaryValue:data valueType:valueType error:error]; + } + + match = [self.dictModelsPatExpression firstMatchInString:className options:0 range:range]; + if (match) { + NSString *valueType = [className substringWithRange:[match rangeAtIndex:2]]; + return [self deserializeDictionaryValue:data valueType:valueType error:error]; + } + + // model + Class ModelClass = NSClassFromString(className); + if ([ModelClass instancesRespondToSelector:@selector(initWithDictionary:error:)]) { + return [(JSONModel *) [ModelClass alloc] initWithDictionary:data error:error]; + } + + if(error) { + *error = [self unknownResponseErrorWithExpectedType:className data:data]; + } + return nil; +} + +- (id) deserializeDictionaryValue:(id) data valueType:(NSString *) valueType error:(NSError**)error { + if(![data isKindOfClass: [NSDictionary class]]) { + if(error) { + *error = [self typeMismatchErrorWithExpectedType:NSStringFromClass([NSDictionary class]) data:data]; + } + return nil; + } + __block NSMutableDictionary *resultDict = [NSMutableDictionary dictionaryWithCapacity:[data count]]; + for (id key in [data allKeys]) { + id obj = [data valueForKey:key]; + id dicObj = [self deserialize:obj class:valueType error:error]; + if(dicObj) { + [resultDict setValue:dicObj forKey:key]; + } else if([obj isKindOfClass:[NSNull class]]) { + if(self.treatNullAsError) { + if (error) { + *error = [self emptyValueOccurredError]; + } + resultDict = nil; + break; + } + } else { + resultDict = nil; + break; + } + } + return resultDict; +} + +- (id) deserializeArrayValue:(id) data innerType:(NSString *) innerType error:(NSError**)error { + if(![data isKindOfClass: [NSArray class]]) { + if(error) { + *error = [self typeMismatchErrorWithExpectedType:NSStringFromClass([NSArray class]) data:data]; + } + return nil; + } + NSMutableArray* resultArray = [NSMutableArray arrayWithCapacity:[data count]]; + for (id obj in data) { + id arrObj = [self deserialize:obj class:innerType error:error]; + if(arrObj) { + [resultArray addObject:arrObj]; + } else if([obj isKindOfClass:[NSNull class]]) { + if(self.treatNullAsError) { + if (error) { + *error = [self emptyValueOccurredError]; + } + resultArray = nil; + break; + } + } else { + resultArray = nil; + break; + } + } + return resultArray; +}; + +- (id) deserializePrimitiveValue:(id) data class:(NSString *) className error:(NSError**)error { + if ([className isEqualToString:@"NSString"]) { + return [NSString stringWithString:data]; + } + else if ([className isEqualToString:@"NSDate"]) { + return [self deserializeDateValue:data error:error]; + } + else if ([className isEqualToString:@"NSNumber"]) { + // NSNumber from NSNumber + if ([data isKindOfClass:[NSNumber class]]) { + return data; + } + else if ([data isKindOfClass:[NSString class]]) { + // NSNumber (NSCFBoolean) from NSString + if ([[data lowercaseString] isEqualToString:@"true"] || [[data lowercaseString] isEqualToString:@"false"]) { + return @([data boolValue]); + // NSNumber from NSString + } else { + NSNumber* formattedValue = [self.numberFormatter numberFromString:data]; + if(!formattedValue && [data length] > 0 && error) { + *error = [self typeMismatchErrorWithExpectedType:className data:data]; + } + return formattedValue; + } + } + } + if(error) { + *error = [self typeMismatchErrorWithExpectedType:className data:data]; + } + return nil; +} + +- (id) deserializeDateValue:(id) data error:(NSError**)error { + NSDate *date =[NSDate dateWithISO8601String:data]; + if(!date && [data length] > 0 && error) { + *error = [self typeMismatchErrorWithExpectedType:NSStringFromClass([NSDate class]) data:data]; + } + return date; +}; + +-(NSError *)typeMismatchErrorWithExpectedType:(NSString *)expected data:(id)data { + NSString * message = [NSString stringWithFormat:NSLocalizedString(@"Received response [%@] is not an object of type %@",nil),data, expected]; + NSDictionary * userInfo = @{NSLocalizedDescriptionKey : message}; + return [NSError errorWithDomain:SWGDeserializationErrorDomainKey code:SWGTypeMismatchErrorCode userInfo:userInfo]; +} + +-(NSError *)emptyValueOccurredError { + NSString * message = NSLocalizedString(@"Received response contains null value in dictionary or array response",nil); + NSDictionary * userInfo = @{NSLocalizedDescriptionKey : message}; + return [NSError errorWithDomain:SWGDeserializationErrorDomainKey code:SWGEmptyValueOccurredErrorCode userInfo:userInfo]; +} + +-(NSError *)unknownResponseErrorWithExpectedType:(NSString *)expected data:(id)data { + NSString * message = [NSString stringWithFormat:NSLocalizedString(@"Unknown response expected type %@ [reponse: %@]",nil),expected,data]; + NSDictionary * userInfo = @{NSLocalizedDescriptionKey : message}; + return [NSError errorWithDomain:SWGDeserializationErrorDomainKey code:SWGUnknownResponseObjectErrorCode userInfo:userInfo]; +} + +@end diff --git a/samples/client/petstore/objc/SwaggerClient/SWGReturn.h b/samples/client/petstore/objc/SwaggerClient/SWGReturn.h deleted file mode 100644 index fcce78f3681..00000000000 --- a/samples/client/petstore/objc/SwaggerClient/SWGReturn.h +++ /dev/null @@ -1,20 +0,0 @@ -#import -#import "SWGObject.h" - -/** - * NOTE: This class is auto generated by the swagger code generator program. - * https://github.com/swagger-api/swagger-codegen - * Do not edit the class manually. - */ - - - -@protocol SWGReturn -@end - -@interface SWGReturn : SWGObject - - -@property(nonatomic) NSNumber* _return; - -@end diff --git a/samples/client/petstore/objc/SwaggerClient/SWGReturn.m b/samples/client/petstore/objc/SwaggerClient/SWGReturn.m deleted file mode 100644 index ad97f1b15e9..00000000000 --- a/samples/client/petstore/objc/SwaggerClient/SWGReturn.m +++ /dev/null @@ -1,51 +0,0 @@ -#import "SWGReturn.h" - -@implementation SWGReturn - -- (instancetype)init { - self = [super init]; - - if (self) { - // initalise property's default value, if any - - } - - return self; -} - - -/** - * Maps json key to property name. - * This method is used by `JSONModel`. - */ -+ (JSONKeyMapper *)keyMapper -{ - return [[JSONKeyMapper alloc] initWithDictionary:@{ @"return": @"_return" }]; -} - -/** - * Indicates whether the property with the given name is optional. - * If `propertyName` is optional, then return `YES`, otherwise return `NO`. - * This method is used by `JSONModel`. - */ -+ (BOOL)propertyIsOptional:(NSString *)propertyName -{ - NSArray *optionalProperties = @[@"_return"]; - - if ([optionalProperties containsObject:propertyName]) { - return YES; - } - else { - return NO; - } -} - -/** - * Gets the string presentation of the object. - * This method will be called when logging model object using `NSLog`. - */ -- (NSString *)description { - return [[self toDictionary] description]; -} - -@end diff --git a/samples/client/petstore/objc/SwaggerClient/SWGSpecialModelName_.h b/samples/client/petstore/objc/SwaggerClient/SWGSpecialModelName_.h deleted file mode 100644 index 88187b28877..00000000000 --- a/samples/client/petstore/objc/SwaggerClient/SWGSpecialModelName_.h +++ /dev/null @@ -1,20 +0,0 @@ -#import -#import "SWGObject.h" - -/** - * NOTE: This class is auto generated by the swagger code generator program. - * https://github.com/swagger-api/swagger-codegen - * Do not edit the class manually. - */ - - - -@protocol SWGSpecialModelName_ -@end - -@interface SWGSpecialModelName_ : SWGObject - - -@property(nonatomic) NSNumber* specialPropertyName; - -@end diff --git a/samples/client/petstore/objc/SwaggerClient/SWGSpecialModelName_.m b/samples/client/petstore/objc/SwaggerClient/SWGSpecialModelName_.m deleted file mode 100644 index 9e1e680787d..00000000000 --- a/samples/client/petstore/objc/SwaggerClient/SWGSpecialModelName_.m +++ /dev/null @@ -1,51 +0,0 @@ -#import "SWGSpecialModelName_.h" - -@implementation SWGSpecialModelName_ - -- (instancetype)init { - self = [super init]; - - if (self) { - // initalise property's default value, if any - - } - - return self; -} - - -/** - * Maps json key to property name. - * This method is used by `JSONModel`. - */ -+ (JSONKeyMapper *)keyMapper -{ - return [[JSONKeyMapper alloc] initWithDictionary:@{ @"$special[property.name]": @"specialPropertyName" }]; -} - -/** - * Indicates whether the property with the given name is optional. - * If `propertyName` is optional, then return `YES`, otherwise return `NO`. - * This method is used by `JSONModel`. - */ -+ (BOOL)propertyIsOptional:(NSString *)propertyName -{ - NSArray *optionalProperties = @[@"specialPropertyName"]; - - if ([optionalProperties containsObject:propertyName]) { - return YES; - } - else { - return NO; - } -} - -/** - * Gets the string presentation of the object. - * This method will be called when logging model object using `NSLog`. - */ -- (NSString *)description { - return [[self toDictionary] description]; -} - -@end diff --git a/samples/client/petstore/objc/SwaggerClient/SWGTag.m b/samples/client/petstore/objc/SwaggerClient/SWGTag.m index 9eb00a157e3..8ad1742750d 100644 --- a/samples/client/petstore/objc/SwaggerClient/SWGTag.m +++ b/samples/client/petstore/objc/SwaggerClient/SWGTag.m @@ -4,12 +4,10 @@ - (instancetype)init { self = [super init]; - if (self) { // initalise property's default value, if any } - return self; } @@ -18,8 +16,7 @@ * Maps json key to property name. * This method is used by `JSONModel`. */ -+ (JSONKeyMapper *)keyMapper -{ ++ (JSONKeyMapper *)keyMapper { return [[JSONKeyMapper alloc] initWithDictionary:@{ @"id": @"_id", @"name": @"name" }]; } @@ -28,24 +25,11 @@ * If `propertyName` is optional, then return `YES`, otherwise return `NO`. * This method is used by `JSONModel`. */ -+ (BOOL)propertyIsOptional:(NSString *)propertyName -{ ++ (BOOL)propertyIsOptional:(NSString *)propertyName { + NSArray *optionalProperties = @[@"_id", @"name"]; + return [optionalProperties containsObject:propertyName]; - if ([optionalProperties containsObject:propertyName]) { - return YES; - } - else { - return NO; - } -} - -/** - * Gets the string presentation of the object. - * This method will be called when logging model object using `NSLog`. - */ -- (NSString *)description { - return [[self toDictionary] description]; } @end diff --git a/samples/client/petstore/objc/SwaggerClient/SWGUser.m b/samples/client/petstore/objc/SwaggerClient/SWGUser.m index 2445dd37854..2551108cb5f 100644 --- a/samples/client/petstore/objc/SwaggerClient/SWGUser.m +++ b/samples/client/petstore/objc/SwaggerClient/SWGUser.m @@ -4,12 +4,10 @@ - (instancetype)init { self = [super init]; - if (self) { // initalise property's default value, if any } - return self; } @@ -18,8 +16,7 @@ * Maps json key to property name. * This method is used by `JSONModel`. */ -+ (JSONKeyMapper *)keyMapper -{ ++ (JSONKeyMapper *)keyMapper { return [[JSONKeyMapper alloc] initWithDictionary:@{ @"id": @"_id", @"username": @"username", @"firstName": @"firstName", @"lastName": @"lastName", @"email": @"email", @"password": @"password", @"phone": @"phone", @"userStatus": @"userStatus" }]; } @@ -28,24 +25,11 @@ * If `propertyName` is optional, then return `YES`, otherwise return `NO`. * This method is used by `JSONModel`. */ -+ (BOOL)propertyIsOptional:(NSString *)propertyName -{ ++ (BOOL)propertyIsOptional:(NSString *)propertyName { + NSArray *optionalProperties = @[@"_id", @"username", @"firstName", @"lastName", @"email", @"password", @"phone", @"userStatus"]; + return [optionalProperties containsObject:propertyName]; - if ([optionalProperties containsObject:propertyName]) { - return YES; - } - else { - return NO; - } -} - -/** - * Gets the string presentation of the object. - * This method will be called when logging model object using `NSLog`. - */ -- (NSString *)description { - return [[self toDictionary] description]; } @end diff --git a/samples/client/petstore/objc/SwaggerClientTests/Tests/DeserializationTest.m b/samples/client/petstore/objc/SwaggerClientTests/Tests/DeserializationTest.m index 5f5d274b4b0..3284336a64b 100644 --- a/samples/client/petstore/objc/SwaggerClientTests/Tests/DeserializationTest.m +++ b/samples/client/petstore/objc/SwaggerClientTests/Tests/DeserializationTest.m @@ -27,11 +27,28 @@ [formatter setDateFormat:@"yyyy-MM-dd"]; NSDate *date = [formatter dateFromString:dateStr]; NSError* error; - NSDate *deserializedDate = [apiClient deserialize:dateStr class:@"NSDate*" error:&error]; + NSDate *deserializedDate = [apiClient.responseDeserializer deserialize:dateStr class:@"NSDate*" error:&error]; XCTAssertNil(error); XCTAssertEqualWithAccuracy([date timeIntervalSinceReferenceDate], [deserializedDate timeIntervalSinceReferenceDate], 0.001); } +- (void)testDeserializeInvalidDate { + NSString *dateStr = @"random string"; + + NSError* error; + NSDate *deserializedDate = [apiClient.responseDeserializer deserialize:dateStr class:@"NSDate*" error:&error]; + XCTAssertNotNil(error); + XCTAssertNil(deserializedDate); +} + +- (void)testDeserializeEmptyDate { + NSString *dateStr = @""; + NSError* error; + NSDate *deserializedDate = [apiClient.responseDeserializer deserialize:dateStr class:@"NSDate*" error:&error]; + XCTAssertNil(error); + XCTAssertNil(deserializedDate); +} + - (void)testDeserializeDateTime { NSString *dateTimeStr = @"1997-07-16T19:20:30+00:00"; @@ -39,15 +56,23 @@ [formatter setDateFormat:@"yyyy-MM-dd'T'HH:mm:ssZ"]; NSDate *dateTime = [formatter dateFromString:dateTimeStr]; NSError* error; - NSDate *deserializedDateTime = [apiClient deserialize:dateTimeStr class:@"NSDate*" error:&error]; + NSDate *deserializedDateTime = [apiClient.responseDeserializer deserialize:dateTimeStr class:@"NSDate*" error:&error]; XCTAssertNil(error); XCTAssertEqualWithAccuracy([dateTime timeIntervalSinceReferenceDate], [deserializedDateTime timeIntervalSinceReferenceDate], 0.001); } +- (void)testDeserializeUnknownObject { + NSString *data = @"random string"; + NSError* error; + NSNumber *result = [apiClient.responseDeserializer deserialize:data class:@"DeserializationTest*" error:&error]; + XCTAssertNotNil(error); + XCTAssertNil(result); +} + - (void)testDeserializeObject { NSNumber *data = @1; NSError* error; - NSNumber *result = [apiClient deserialize:data class:@"NSObject*" error:&error]; + NSNumber *result = [apiClient.responseDeserializer deserialize:data class:@"NSObject*" error:&error]; XCTAssertNil(error); XCTAssertEqualObjects(data, result); } @@ -55,7 +80,7 @@ - (void)testDeserializeString { NSString *data = @"test string"; NSError* error; - NSString *result = [apiClient deserialize:data class:@"NSString*" error:&error]; + NSString *result = [apiClient.responseDeserializer deserialize:data class:@"NSString*" error:&error]; XCTAssertNil(error); XCTAssertTrue([result isEqualToString:data]); } @@ -63,12 +88,29 @@ - (void)testDeserializeListOfString { NSArray *data = @[@"test string"]; NSError* error; - NSArray *result = [apiClient deserialize:data class:@"NSArray* /* NSString */" error:&error]; + NSArray *result = [apiClient.responseDeserializer deserialize:data class:@"NSArray*" error:&error]; XCTAssertNil(error); XCTAssertTrue([result isKindOfClass:[NSArray class]]); XCTAssertTrue([result[0] isKindOfClass:[NSString class]]); } +- (void)testDeserializeInvalidListOfNumbers { + NSArray *data = @[@"test string"]; + NSError* error; + NSArray *result = [apiClient.responseDeserializer deserialize:data class:@"NSArray*" error:&error]; + XCTAssertNotNil(error); + XCTAssertNil(result); +} + +- (void)testDeserializeListOfNumbers { + NSArray *data = @[@"1.0"]; + NSError* error; + NSArray *result = [apiClient.responseDeserializer deserialize:data class:@"NSArray*" error:&error]; + XCTAssertNil(error); + XCTAssertTrue([result isKindOfClass:[NSArray class]]); + XCTAssertTrue([result[0] isKindOfClass:[NSNumber class]]); +} + - (void)testDeserializeListOfModels { NSArray *data = @[ @@ -92,7 +134,7 @@ }]; NSError* error; - NSArray *result = [apiClient deserialize:data class:@"NSArray*" error:&error]; + NSArray *result = [apiClient.responseDeserializer deserialize:data class:@"NSArray*" error:&error]; XCTAssertTrue([result isKindOfClass:[NSArray class]]); XCTAssertTrue([[result firstObject] isKindOfClass:[SWGPet class]]); @@ -123,7 +165,7 @@ } }; NSError* error; - NSDictionary *result = [apiClient deserialize:data class:@"NSDictionary* /* NSString, SWGPet */" error:&error]; + NSDictionary *result = [apiClient.responseDeserializer deserialize:data class:@"NSDictionary* /* NSString, SWGPet */" error:&error]; XCTAssertTrue([result isKindOfClass:[NSDictionary class]]); XCTAssertTrue([result[@"pet"] isKindOfClass:[SWGPet class]]); @@ -134,11 +176,44 @@ NSDictionary *data = @{ @"foo": @{ - @"bar": @1 + @"bar": @1, + @"bar2": [NSNull null] } }; + SWGResponseDeserializer* responseDeserializer = [[SWGResponseDeserializer alloc] init]; NSError* error; - NSDictionary *result = [apiClient deserialize:data class:@"NSDictionary* /* NSString, NSDictionary* /* NSString, NSNumber */ */" error:&error]; + NSDictionary *result = [responseDeserializer deserialize:data class:@"NSDictionary* /* NSString, NSDictionary* /* NSString, NSNumber */ */" error:&error]; + + XCTAssertTrue([result isKindOfClass:[NSDictionary class]]); + XCTAssertTrue([result[@"foo"] isKindOfClass:[NSDictionary class]]); + XCTAssertTrue([result[@"foo"][@"bar"] isKindOfClass:[NSNumber class]]); +} + + +- (void)testDeserializeNestedMapWithNullValue { + NSDictionary *data = + @{ + @"foo": @{ + @"bar": @1, + @"bar2": [NSNull null] + } + }; + SWGResponseDeserializer* responseDeserializer = [[SWGResponseDeserializer alloc] init]; + responseDeserializer.treatNullAsError = YES; + NSError* error; + NSDictionary *result = [responseDeserializer deserialize:data class:@"NSDictionary* /* NSString, NSDictionary* /* NSString, NSNumber */ */" error:&error]; + XCTAssertNil(result); + XCTAssertNotNil(error); +} + +- (void)testDeserializeNestedMap2 { + NSDictionary *data = @{ + @"foo": @{ + @"bar": @1 + } + }; + NSError* error; + NSDictionary *result = [apiClient.responseDeserializer deserialize:data class:@"NSDictionary*>*" error:&error]; XCTAssertTrue([result isKindOfClass:[NSDictionary class]]); XCTAssertTrue([result[@"foo"] isKindOfClass:[NSDictionary class]]); @@ -148,7 +223,7 @@ - (void)testDeserializeNestedList { NSArray *data = @[@[@"foo"]]; NSError* error; - NSArray *result = [apiClient deserialize:data class:@"NSArray* /* NSArray* /* NSString */ */" error:&error]; + NSArray *result = [apiClient.responseDeserializer deserialize:data class:@"NSArray* /* NSArray* /* NSString */ */" error:&error]; XCTAssertTrue([result isKindOfClass:[NSArray class]]); XCTAssertTrue([result[0] isKindOfClass:[NSArray class]]); @@ -161,11 +236,11 @@ data = @"true"; NSError* error; - result = [apiClient deserialize:data class:@"NSNumber*" error:&error]; + result = [apiClient.responseDeserializer deserialize:data class:@"NSNumber*" error:&error]; XCTAssertTrue([result isEqual:@YES]); data = @"false"; - result = [apiClient deserialize:data class:@"NSNumber*" error:&error]; + result = [apiClient.responseDeserializer deserialize:data class:@"NSNumber*" error:&error]; XCTAssertTrue([result isEqual:@NO]); } diff --git a/samples/client/petstore/objc/SwaggerClientTests/Tests/PetApiTest.m b/samples/client/petstore/objc/SwaggerClientTests/Tests/PetApiTest.m index 7fd3d4f97c8..7ddc817e9ef 100644 --- a/samples/client/petstore/objc/SwaggerClientTests/Tests/PetApiTest.m +++ b/samples/client/petstore/objc/SwaggerClientTests/Tests/PetApiTest.m @@ -165,7 +165,7 @@ which causes an exception when deserializing the data SWGTag* tag = [[SWGTag alloc] init]; tag.name = @"tony"; NSLog(@"%@", pet._id); - pet.tags = [[NSArray alloc] initWithObjects:tag, nil]; + pet.tags = (id)[[NSArray alloc] initWithObjects:tag, nil]; [api addPetWithBody:pet completionHandler:^(NSError *error) { if(error) { diff --git a/samples/client/petstore/objc/git_push.sh b/samples/client/petstore/objc/git_push.sh index 1a36388db02..ed374619b13 100644 --- a/samples/client/petstore/objc/git_push.sh +++ b/samples/client/petstore/objc/git_push.sh @@ -8,12 +8,12 @@ git_repo_id=$2 release_note=$3 if [ "$git_user_id" = "" ]; then - git_user_id="YOUR_GIT_USR_ID" + git_user_id="GIT_USER_ID" echo "[INFO] No command line input provided. Set \$git_user_id to $git_user_id" fi if [ "$git_repo_id" = "" ]; then - git_repo_id="YOUR_GIT_REPO_ID" + git_repo_id="GIT_REPO_ID" echo "[INFO] No command line input provided. Set \$git_repo_id to $git_repo_id" fi