#import "SWGApiClient.h" NSString *const SWGResponseObjectErrorKey = @"SWGResponseObject"; static long requestId = 0; static bool offlineState = false; static NSMutableSet * queuedRequests = nil; static bool cacheEnabled = false; static AFNetworkReachabilityStatus reachabilityStatus = AFNetworkReachabilityStatusNotReachable; static void (^reachabilityChangeBlock)(int); @interface SWGApiClient () @property (readwrite, nonatomic) NSDictionary *HTTPResponseHeaders; @end @implementation SWGApiClient - (instancetype)init { NSString *baseUrl = [[SWGConfiguration sharedConfig] host]; return [self initWithBaseURL:[NSURL URLWithString:baseUrl]]; } - (instancetype)initWithBaseURL:(NSURL *)url { self = [super initWithBaseURL:url]; if (self) { self.requestSerializer = [AFJSONRequestSerializer serializer]; self.responseSerializer = [AFJSONResponseSerializer serializer]; self.securityPolicy = [self customSecurityPolicy]; // configure reachability [self configureCacheReachibility]; } return self; } + (void)initialize { if (self == [SWGApiClient class]) { queuedRequests = [[NSMutableSet alloc] init]; // initialize URL cache [self configureCacheWithMemoryAndDiskCapacity:4*1024*1024 diskSize:32*1024*1024]; } } #pragma mark - Setter Methods + (void) setOfflineState:(BOOL) state { offlineState = state; } + (void) setCacheEnabled:(BOOL)enabled { cacheEnabled = enabled; } - (void)setHeaderValue:(NSString*) value forKey:(NSString*) forKey { [self.requestSerializer setValue:value forHTTPHeaderField:forKey]; } #pragma mark - Log Methods + (void)debugLog:(NSString *)method message:(NSString *)format, ... { SWGConfiguration *config = [SWGConfiguration sharedConfig]; if (!config.debug) { return; } NSMutableString *message = [NSMutableString stringWithCapacity:1]; if (method) { [message appendString:[NSString stringWithFormat:@"%@: ", method]]; } va_list args; va_start(args, format); [message appendString:[[NSString alloc] initWithFormat:format arguments:args]]; // If set logging file handler, log into file, // otherwise log into console. if (config.loggingFileHanlder) { [config.loggingFileHanlder seekToEndOfFile]; [config.loggingFileHanlder writeData:[message dataUsingEncoding:NSUTF8StringEncoding]]; } else { NSLog(@"%@", message); } va_end(args); } - (void)logResponse:(AFHTTPRequestOperation *)operation forRequest:(NSURLRequest *)request error:(NSError*)error { NSString *message = [NSString stringWithFormat:@"\n[DEBUG] HTTP request body \n~BEGIN~\n %@\n~END~\n"\ "[DEBUG] HTTP response body \n~BEGIN~\n %@\n~END~\n", [[NSString alloc] initWithData:request.HTTPBody encoding:NSUTF8StringEncoding], operation.responseString]; SWGDebugLog(message); } #pragma mark - Cache Methods +(void)clearCache { [[NSURLCache sharedURLCache] removeAllCachedResponses]; } +(void)configureCacheWithMemoryAndDiskCapacity: (unsigned long) memorySize diskSize: (unsigned long) diskSize { NSAssert(memorySize > 0, @"invalid in-memory cache size"); NSAssert(diskSize >= 0, @"invalid disk cache size"); NSURLCache *cache = [[NSURLCache alloc] initWithMemoryCapacity:memorySize diskCapacity:diskSize diskPath:@"swagger_url_cache"]; [NSURLCache setSharedURLCache:cache]; } #pragma mark - Utility Methods /* * Detect `Accept` from accepts */ + (NSString *) selectHeaderAccept:(NSArray *)accepts { if (accepts == nil || [accepts count] == 0) { return @""; } NSMutableArray *lowerAccepts = [[NSMutableArray alloc] initWithCapacity:[accepts count]]; [accepts enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) { [lowerAccepts addObject:[obj lowercaseString]]; }]; if ([lowerAccepts containsObject:@"application/json"]) { return @"application/json"; } else { return [lowerAccepts componentsJoinedByString:@", "]; } } /* * Detect `Content-Type` from contentTypes */ + (NSString *) selectHeaderContentType:(NSArray *)contentTypes { if (contentTypes == nil || [contentTypes count] == 0) { return @"application/json"; } NSMutableArray *lowerContentTypes = [[NSMutableArray alloc] initWithCapacity:[contentTypes count]]; [contentTypes enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) { [lowerContentTypes addObject:[obj lowercaseString]]; }]; if ([lowerContentTypes containsObject:@"application/json"]) { return @"application/json"; } else { return lowerContentTypes[0]; } } + (NSString*)escape:(id)unescaped { if([unescaped isKindOfClass:[NSString class]]){ return (NSString *)CFBridgingRelease (CFURLCreateStringByAddingPercentEscapes( NULL, (__bridge CFStringRef) unescaped, NULL, (CFStringRef)@"!*'();:@&=+$,/?%#[]", kCFStringEncodingUTF8)); } else { return [NSString stringWithFormat:@"%@", unescaped]; } } #pragma mark - Request Methods +(unsigned long)requestQueueSize { return [queuedRequests count]; } +(NSNumber*) nextRequestId { @synchronized(self) { long nextId = ++requestId; SWGDebugLog(@"got id %ld", nextId); return [NSNumber numberWithLong:nextId]; } } +(NSNumber*) queueRequest { NSNumber* requestId = [SWGApiClient nextRequestId]; SWGDebugLog(@"added %@ to request queue", requestId); [queuedRequests addObject:requestId]; return requestId; } +(void) cancelRequest:(NSNumber*)requestId { [queuedRequests removeObject:requestId]; } -(Boolean) executeRequestWithId:(NSNumber*) requestId { NSSet* matchingItems = [queuedRequests objectsPassingTest:^BOOL(id obj, BOOL *stop) { if([obj intValue] == [requestId intValue]) { return YES; } else { return NO; } }]; if(matchingItems.count == 1) { SWGDebugLog(@"removed request id %@", requestId); [queuedRequests removeObject:requestId]; return YES; } else { return NO; } } #pragma mark - Reachability Methods +(AFNetworkReachabilityStatus) getReachabilityStatus { return reachabilityStatus; } +(void) setReachabilityChangeBlock:(void(^)(int))changeBlock { reachabilityChangeBlock = changeBlock; } - (void) configureCacheReachibility { [self.reachabilityManager setReachabilityStatusChangeBlock:^(AFNetworkReachabilityStatus status) { reachabilityStatus = status; switch (status) { case AFNetworkReachabilityStatusUnknown: SWGDebugLog(@"reachability changed to AFNetworkReachabilityStatusUnknown"); [SWGApiClient setOfflineState:true]; break; case AFNetworkReachabilityStatusNotReachable: SWGDebugLog(@"reachability changed to AFNetworkReachabilityStatusNotReachable"); [SWGApiClient setOfflineState:true]; break; case AFNetworkReachabilityStatusReachableViaWWAN: SWGDebugLog(@"reachability changed to AFNetworkReachabilityStatusReachableViaWWAN"); [SWGApiClient setOfflineState:false]; break; case AFNetworkReachabilityStatusReachableViaWiFi: SWGDebugLog(@"reachability changed to AFNetworkReachabilityStatusReachableViaWiFi"); [SWGApiClient setOfflineState:false]; break; default: break; } // call the reachability block, if configured if(reachabilityChangeBlock != nil) { reachabilityChangeBlock(status); } }]; [self.reachabilityManager startMonitoring]; } #pragma mark - Deserialize methods - (id) deserialize:(id) data class:(NSString *) class { NSRegularExpression *regexp = nil; NSTextCheckingResult *match = nil; NSMutableArray *resultArray = nil; NSMutableDictionary *resultDict = nil; NSString *innerType = nil; // 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; } // 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) { NSArray *dataArray = data; innerType = [class substringWithRange:[match rangeAtIndex:1]]; resultArray = [NSMutableArray arrayWithCapacity:[dataArray count]]; [data enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) { [resultArray addObject:[self deserialize:obj class:innerType]]; } ]; 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) { [resultArray addObject:[self deserialize:obj class:innerType]]; }]; 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) { [resultDict setValue:[self deserialize:obj class:valueType] forKey:key]; }]; 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 [[ModelClass alloc] initWithDictionary:data error:nil]; } return nil; } #pragma mark - Operation Methods - (void) operationWithCompletionBlock: (NSURLRequest *)request requestId: (NSNumber *) requestId completionBlock: (void (^)(id, NSError *))completionBlock { AFHTTPRequestOperation *op = [self HTTPRequestOperationWithRequest:request success:^(AFHTTPRequestOperation *operation, id response) { if([self executeRequestWithId:requestId]) { [self logResponse:operation forRequest:request error:nil]; NSDictionary *responseHeaders = [[operation response] allHeaderFields]; self.HTTPResponseHeaders = responseHeaders; completionBlock(response, nil); } } failure:^(AFHTTPRequestOperation *operation, NSError *error) { if([self executeRequestWithId:requestId]) { NSMutableDictionary *userInfo = [error.userInfo mutableCopy]; if(operation.responseObject) { // Add in the (parsed) response body. userInfo[SWGResponseObjectErrorKey] = operation.responseObject; } NSError *augmentedError = [error initWithDomain:error.domain code:error.code userInfo:userInfo]; [self logResponse:nil forRequest:request error:augmentedError]; NSDictionary *responseHeaders = [[operation response] allHeaderFields]; self.HTTPResponseHeaders = responseHeaders; completionBlock(nil, augmentedError); } }]; [self.operationQueue addOperation:op]; } - (void) downloadOperationWithCompletionBlock: (NSURLRequest *)request requestId: (NSNumber *) requestId completionBlock: (void (^)(id, NSError *))completionBlock { AFHTTPRequestOperation *op = [self HTTPRequestOperationWithRequest:request success:^(AFHTTPRequestOperation *operation, id responseObject) { SWGConfiguration *config = [SWGConfiguration sharedConfig]; NSString *directory = nil; if (config.tempFolderPath) { directory = config.tempFolderPath; } else { directory = NSTemporaryDirectory(); } NSDictionary *headers = operation.response.allHeaderFields; NSString *filename = nil; if ([headers objectForKey:@"Content-Disposition"]) { NSString *pattern = @"filename=['\"]?([^'\"\\s]+)['\"]?"; NSRegularExpression *regexp = [NSRegularExpression regularExpressionWithPattern:pattern options:NSRegularExpressionCaseInsensitive error:nil]; NSString *contentDispositionHeader = [headers objectForKey:@"Content-Disposition"]; NSTextCheckingResult *match = [regexp firstMatchInString:contentDispositionHeader options:0 range:NSMakeRange(0, [contentDispositionHeader length])]; filename = [contentDispositionHeader substringWithRange:[match rangeAtIndex:1]]; } else { filename = [NSString stringWithFormat:@"%@", [[NSProcessInfo processInfo] globallyUniqueString]]; } NSString *filepath = [directory stringByAppendingPathComponent:filename]; NSURL *file = [NSURL fileURLWithPath:filepath]; [operation.responseData writeToURL:file atomically:YES]; self.HTTPResponseHeaders = headers; completionBlock(file, nil); } failure:^(AFHTTPRequestOperation *operation, NSError *error) { if ([self executeRequestWithId:requestId]) { NSMutableDictionary *userInfo = [error.userInfo mutableCopy]; if (operation.responseObject) { userInfo[SWGResponseObjectErrorKey] = operation.responseObject; } NSError *augmentedError = [error initWithDomain:error.domain code:error.code userInfo:userInfo]; [self logResponse:nil forRequest:request error:augmentedError]; NSDictionary *responseHeaders = [[operation response] allHeaderFields]; self.HTTPResponseHeaders = responseHeaders; completionBlock(nil, augmentedError); } }]; [self.operationQueue addOperation:op]; } #pragma mark - Perform Request Methods -(NSNumber*) requestWithCompletionBlock: (NSString*) path method: (NSString*) method pathParams: (NSDictionary *) pathParams queryParams: (NSDictionary*) queryParams formParams: (NSDictionary *) formParams files: (NSDictionary *) files body: (id) body headerParams: (NSDictionary*) headerParams authSettings: (NSArray *) authSettings requestContentType: (NSString*) requestContentType responseContentType: (NSString*) responseContentType responseType: (NSString *) responseType completionBlock: (void (^)(id, NSError *))completionBlock { // setting request serializer if ([requestContentType isEqualToString:@"application/json"]) { self.requestSerializer = [SWGJSONRequestSerializer serializer]; } else if ([requestContentType isEqualToString:@"application/x-www-form-urlencoded"]) { self.requestSerializer = [AFHTTPRequestSerializer serializer]; } else if ([requestContentType isEqualToString:@"multipart/form-data"]) { self.requestSerializer = [AFHTTPRequestSerializer serializer]; } else { NSAssert(false, @"unsupport request type %@", requestContentType); } // setting response serializer if ([responseContentType isEqualToString:@"application/json"]) { self.responseSerializer = [SWGJSONResponseSerializer serializer]; } else { self.responseSerializer = [AFHTTPResponseSerializer serializer]; } // sanitize parameters pathParams = [self sanitizeForSerialization:pathParams]; queryParams = [self sanitizeForSerialization:queryParams]; headerParams = [self sanitizeForSerialization:headerParams]; formParams = [self sanitizeForSerialization:formParams]; body = [self sanitizeForSerialization:body]; // auth setting [self updateHeaderParams:&headerParams queryParams:&queryParams WithAuthSettings:authSettings]; NSMutableString *resourcePath = [NSMutableString stringWithString:path]; [pathParams enumerateKeysAndObjectsUsingBlock:^(id key, id obj, BOOL *stop) { [resourcePath replaceCharactersInRange:[resourcePath rangeOfString:[NSString stringWithFormat:@"%@%@%@", @"{", key, @"}"]] withString:[SWGApiClient escape:obj]]; }]; NSMutableURLRequest * request = nil; NSString* pathWithQueryParams = [self pathWithQueryParamsToString:resourcePath queryParams:queryParams]; if ([pathWithQueryParams hasPrefix:@"/"]) { pathWithQueryParams = [pathWithQueryParams substringFromIndex:1]; } NSString* urlString = [[NSURL URLWithString:pathWithQueryParams relativeToURL:self.baseURL] absoluteString]; if (files.count > 0) { request = [self.requestSerializer multipartFormRequestWithMethod:@"POST" URLString:urlString parameters:nil constructingBodyWithBlock:^(id formData) { [formParams enumerateKeysAndObjectsUsingBlock:^(id key, id obj, BOOL *stop) { NSString *objString = [self parameterToString:obj]; NSData *data = [objString dataUsingEncoding:NSUTF8StringEncoding]; [formData appendPartWithFormData:data name:key]; }]; [files enumerateKeysAndObjectsUsingBlock:^(id key, id obj, BOOL *stop) { NSURL *filePath = (NSURL *)obj; [formData appendPartWithFileURL:filePath name:key error:nil]; }]; } error:nil]; } else { if (formParams) { request = [self.requestSerializer requestWithMethod:method URLString:urlString parameters:formParams error:nil]; } if (body) { request = [self.requestSerializer requestWithMethod:method URLString:urlString parameters:body error:nil]; } } // request cache BOOL hasHeaderParams = false; if(headerParams != nil && [headerParams count] > 0) { hasHeaderParams = true; } if(offlineState) { SWGDebugLog(@"%@ cache forced", resourcePath); [request setCachePolicy:NSURLRequestReturnCacheDataDontLoad]; } else if(!hasHeaderParams && [method isEqualToString:@"GET"] && cacheEnabled) { SWGDebugLog(@"%@ cache enabled", resourcePath); [request setCachePolicy:NSURLRequestUseProtocolCachePolicy]; } else { SWGDebugLog(@"%@ cache disabled", resourcePath); [request setCachePolicy:NSURLRequestReloadIgnoringLocalCacheData]; } if(hasHeaderParams){ for(NSString * key in [headerParams keyEnumerator]){ [request setValue:[headerParams valueForKey:key] forHTTPHeaderField:key]; } } [self.requestSerializer setValue:responseContentType forHTTPHeaderField:@"Accept"]; // Always disable cookies! [request setHTTPShouldHandleCookies:NO]; NSNumber* requestId = [SWGApiClient queueRequest]; if ([responseType isEqualToString:@"NSURL*"]) { [self downloadOperationWithCompletionBlock:request requestId:requestId completionBlock:^(id data, NSError *error) { completionBlock(data, error); }]; } else { [self operationWithCompletionBlock:request requestId:requestId completionBlock:^(id data, NSError *error) { completionBlock([self deserialize:data class:responseType], error); }]; } return requestId; } #pragma mark - - (NSString*) pathWithQueryParamsToString:(NSString*) path queryParams:(NSDictionary*) queryParams { NSString * separator = nil; int counter = 0; NSMutableString * requestUrl = [NSMutableString stringWithFormat:@"%@", path]; if(queryParams != nil){ for(NSString * key in [queryParams keyEnumerator]){ if(counter == 0) separator = @"?"; else separator = @"&"; id queryParam = [queryParams valueForKey:key]; if([queryParam isKindOfClass:[NSString class]]){ [requestUrl appendString:[NSString stringWithFormat:@"%@%@=%@", separator, [SWGApiClient escape:key], [SWGApiClient escape:[queryParams valueForKey:key]]]]; } else if([queryParam isKindOfClass:[SWGQueryParamCollection class]]){ SWGQueryParamCollection * coll = (SWGQueryParamCollection*) queryParam; NSArray* values = [coll values]; NSString* format = [coll format]; if([format isEqualToString:@"csv"]) { [requestUrl appendString:[NSString stringWithFormat:@"%@%@=%@", separator, [SWGApiClient escape:key], [NSString stringWithFormat:@"%@", [values componentsJoinedByString:@","]]]]; } else if([format isEqualToString:@"tsv"]) { [requestUrl appendString:[NSString stringWithFormat:@"%@%@=%@", separator, [SWGApiClient escape:key], [NSString stringWithFormat:@"%@", [values componentsJoinedByString:@"\t"]]]]; } else if([format isEqualToString:@"pipes"]) { [requestUrl appendString:[NSString stringWithFormat:@"%@%@=%@", separator, [SWGApiClient escape:key], [NSString stringWithFormat:@"%@", [values componentsJoinedByString:@"|"]]]]; } else if([format isEqualToString:@"multi"]) { for(id obj in values) { [requestUrl appendString:[NSString stringWithFormat:@"%@%@=%@", separator, [SWGApiClient escape:key], [NSString stringWithFormat:@"%@", obj]]]; counter += 1; } } } else { [requestUrl appendString:[NSString stringWithFormat:@"%@%@=%@", separator, [SWGApiClient escape:key], [NSString stringWithFormat:@"%@", [queryParams valueForKey:key]]]]; } counter += 1; } } return requestUrl; } /** * Update header and query params based on authentication settings */ - (void) updateHeaderParams:(NSDictionary *__autoreleasing *)headers queryParams:(NSDictionary *__autoreleasing *)querys WithAuthSettings:(NSArray *)authSettings { if (!authSettings || [authSettings count] == 0) { return; } NSMutableDictionary *headersWithAuth = [NSMutableDictionary dictionaryWithDictionary:*headers]; NSMutableDictionary *querysWithAuth = [NSMutableDictionary dictionaryWithDictionary:*querys]; SWGConfiguration *config = [SWGConfiguration sharedConfig]; for (NSString *auth in authSettings) { NSDictionary *authSetting = [[config authSettings] objectForKey:auth]; if (authSetting) { if ([authSetting[@"in"] isEqualToString:@"header"]) { [headersWithAuth setObject:authSetting[@"value"] forKey:authSetting[@"key"]]; } else if ([authSetting[@"in"] isEqualToString:@"query"]) { [querysWithAuth setObject:authSetting[@"value"] forKey:authSetting[@"key"]]; } } } *headers = [NSDictionary dictionaryWithDictionary:headersWithAuth]; *querys = [NSDictionary dictionaryWithDictionary:querysWithAuth]; } - (id) sanitizeForSerialization:(id) object { if (object == nil) { return nil; } else if ([object isKindOfClass:[NSString class]] || [object isKindOfClass:[NSNumber class]] || [object isKindOfClass:[SWGQueryParamCollection class]]) { return object; } else if ([object isKindOfClass:[NSDate class]]) { return [object ISO8601String]; } else if ([object isKindOfClass:[NSArray class]]) { NSArray *objectArray = object; NSMutableArray *sanitizedObjs = [NSMutableArray arrayWithCapacity:[objectArray count]]; [object enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) { if (obj) { [sanitizedObjs addObject:[self sanitizeForSerialization:obj]]; } }]; return sanitizedObjs; } else if ([object isKindOfClass:[NSDictionary class]]) { NSDictionary *objectDict = object; NSMutableDictionary *sanitizedObjs = [NSMutableDictionary dictionaryWithCapacity:[objectDict count]]; [object enumerateKeysAndObjectsUsingBlock:^(id key, id obj, BOOL *stop) { if (obj) { [sanitizedObjs setValue:[self sanitizeForSerialization:obj] forKey:key]; } }]; return sanitizedObjs; } else if ([object isKindOfClass:[SWGObject class]]) { return [object toDictionary]; } else { NSException *e = [NSException exceptionWithName:@"InvalidObjectArgumentException" reason:[NSString stringWithFormat:@"*** The argument object: %@ is invalid", object] userInfo:nil]; @throw e; } } - (AFSecurityPolicy *) customSecurityPolicy { AFSecurityPolicy *securityPolicy = [AFSecurityPolicy policyWithPinningMode:AFSSLPinningModeNone]; SWGConfiguration *config = [SWGConfiguration sharedConfig]; if (config.sslCaCert) { NSData *certData = [NSData dataWithContentsOfFile:config.sslCaCert]; [securityPolicy setPinnedCertificates:@[certData]]; } if (config.verifySSL) { [securityPolicy setAllowInvalidCertificates:NO]; } else { [securityPolicy setAllowInvalidCertificates:YES]; [securityPolicy setValidatesDomainName:NO]; } return securityPolicy; } - (NSString *) parameterToString:(id)param { if ([param isKindOfClass:[NSString class]]) { return param; } else if ([param isKindOfClass:[NSNumber class]]) { return [param stringValue]; } else if ([param isKindOfClass:[NSDate class]]) { return [param ISO8601String]; } else if ([param isKindOfClass:[NSArray class]]) { NSMutableArray *mutableParam; [param enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) { [mutableParam addObject:[self parameterToString:obj]]; }]; return [mutableParam componentsJoinedByString:@","]; } else { NSException *e = [NSException exceptionWithName:@"InvalidObjectArgumentException" reason:[NSString stringWithFormat:@"*** The argument object: %@ is invalid", param] userInfo:nil]; @throw e; } } @end