diff --git a/TMTumblrSDK/Core/TMSDKFunctions.m b/TMTumblrSDK/Core/TMSDKFunctions.m index 57618f9b..af4ad484 100644 --- a/TMTumblrSDK/Core/TMSDKFunctions.m +++ b/TMTumblrSDK/Core/TMSDKFunctions.m @@ -17,12 +17,12 @@ @implementation TMSDKFunctions NSString *TMURLEncode(id value) { NSString *string; - + if ([value isKindOfClass:[NSString class]]) string = (NSString *)value; else string = [value stringValue]; - + return (NSString *)CFBridgingRelease(CFURLCreateStringByAddingPercentEscapes(NULL, (CFStringRef)string, NULL, CFSTR("!*'();:@&=+$,/?%#[]%"), kCFStringEncodingUTF8)); } @@ -30,38 +30,56 @@ @implementation TMSDKFunctions NSDictionary *TMQueryStringToDictionary(NSString *query) { NSMutableDictionary *mutableParameterDictionary = [[NSMutableDictionary alloc] init]; - NSArray *parameters = [query componentsSeparatedByString:@"&"]; + NSString *regexStr = @".*\\[[0-9]+\\]$"; + NSRegularExpression *regex = [[NSRegularExpression alloc] initWithPattern:regexStr options:NSRegularExpressionAnchorsMatchLines error:nil]; + NSString *regexKeyStr = @"\\[[0-9]+\\]$"; + NSRegularExpression *replaceKeyRegex = [[NSRegularExpression alloc] initWithPattern:regexKeyStr options:NSRegularExpressionAnchorsMatchLines error:nil]; + + NSArray *parameters = [query componentsSeparatedByString:@"&"]; for (NSString *parameter in parameters) { NSArray *keyValuePair = [parameter componentsSeparatedByString:@"="]; - + if (keyValuePair.count == 2) { NSString *key = TMURLDecode(keyValuePair[0]); NSString *value = TMURLDecode(keyValuePair[1]); + NSUInteger matches = [regex numberOfMatchesInString:key options:NSMatchingReportProgress range:NSMakeRange(0, key.length)]; + BOOL isArray = matches == 1; + if (isArray) { + key = [replaceKeyRegex stringByReplacingMatchesInString:key options:NSMatchingReportProgress range:NSMakeRange(0, key.length) withTemplate:@""]; + } + id existingValueForKey = mutableParameterDictionary[key]; - + if (existingValueForKey) { - if ([existingValueForKey isKindOfClass:[NSMutableArray class]]) + if ([existingValueForKey isKindOfClass:[NSMutableArray class]]) { [(NSMutableArray *)existingValueForKey addObject:value]; - else + } + else { [mutableParameterDictionary setObject:[NSMutableArray arrayWithObjects:existingValueForKey, value, nil] forKey:key]; - } else + } + } + else if (isArray) { + [mutableParameterDictionary setObject:[NSMutableArray arrayWithObject:value] forKey:key]; + } + else { [mutableParameterDictionary setObject:value forKey:key]; + } } } - + return [NSDictionary dictionaryWithDictionary:mutableParameterDictionary]; } NSString *TMDictionaryToQueryString(NSDictionary *dictionary) { NSMutableArray *parameters = [NSMutableArray array]; - + for (NSString *key in [[dictionary allKeys] sortedArrayUsingSelector:@selector(caseInsensitiveCompare:)]) { TMAddKeyValuePairToQueryStringMutableArray(key, dictionary[key], parameters); } - + return [parameters componentsJoinedByString:@"&"]; } @@ -75,9 +93,9 @@ void TMAddKeyValuePairToQueryStringMutableArray(NSString *key, id value, NSMutab } } else if ([value isKindOfClass:[NSArray class]]) { - for (id arrayValue in (NSArray *)value){ - TMAddKeyValuePairToQueryStringMutableArray(key, arrayValue, parameters); - } + [value enumerateObjectsUsingBlock:^(id _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) { + TMAddKeyValuePairToQueryStringMutableArray([NSString stringWithFormat:@"%@[%ld]", key, idx], obj, parameters); + }]; } else { [parameters addObject:[NSString stringWithFormat:@"%@=%@", TMURLEncode(key), TMURLEncode(value)]]; diff --git a/Tests/TMSDKFunctionsTest.m b/Tests/TMSDKFunctionsTest.m index e7146dae..9fa01e26 100644 --- a/Tests/TMSDKFunctionsTest.m +++ b/Tests/TMSDKFunctionsTest.m @@ -59,7 +59,7 @@ - (void)testDictionaryToQueryWithRepeatedParameter { NSString *title = @"Some $$$@#?@#9i==%&&&&title"; NSArray *tags = @[@"adioj ASD $**$*$ a8aA//&&adsijd====", @"lol", @" foo bar baz "]; - NSString *result = [NSString stringWithFormat:@"tag=%@&tag=%@&tag=%@&title=%@", TMURLEncode(tags[0]), + NSString *result = [NSString stringWithFormat:@"tag%%5B0%%5D=%@&tag%%5B1%%5D=%@&tag%%5B2%%5D=%@&title=%@", TMURLEncode(tags[0]), TMURLEncode(tags[1]), TMURLEncode(tags[2]), TMURLEncode(title)]; XCTAssertEqualObjects(TMDictionaryToQueryString(@{ @"title" : title, @"tag" : tags }), result, @@ -76,7 +76,7 @@ - (void)testDictionaryToQueryWithNestedDictionary { }, }; - NSString *expected = [NSString stringWithFormat:@"arrayKey=arrayValue1&arrayKey=arrayValue2&%@=innerDictionaryValue1&%@=innerDictionaryValue2&stringKey=value", + NSString *expected = [NSString stringWithFormat:@"arrayKey%%5B0%%5D=arrayValue1&arrayKey%%5B1%%5D=arrayValue2&%@=innerDictionaryValue1&%@=innerDictionaryValue2&stringKey=value", TMURLEncode(@"dictionaryKey[innerDictionaryKey1]"), TMURLEncode(@"dictionaryKey[innerDictionaryKey2]")]; @@ -85,4 +85,35 @@ - (void)testDictionaryToQueryWithNestedDictionary { XCTAssertEqualObjects(expected, actual); } +- (void)testAcceptsSquareBrackets { + NSString *validKey = @"[]keyIsValid"; + NSString *validValue = @""; + NSString *validKeyTwo = @"key[0]a"; + NSDictionary *input = @{ validKey: validValue, validKeyTwo: validValue }; + + NSString *expectedStr = [NSString stringWithFormat:@"%@=%@&%@=%@", TMURLEncode(validKey), TMURLEncode(validValue), TMURLEncode(validKeyTwo), TMURLEncode(validValue)]; + NSString *actualStr = TMDictionaryToQueryString(input); + + NSDictionary *expectedDict = TMQueryStringToDictionary(expectedStr); + + XCTAssertTrue([expectedStr isEqualToString:actualStr]); + XCTAssertEqualObjects(input, expectedDict); +} + +- (void)testKeysAreTheSame { + NSString *key = @"key"; + + NSString *arrayIn1 = @"1"; + NSString *arrayIn2 = @"2"; + NSString *arrayIn3 = @"3"; + + NSDictionary *input = @{ key: @[ arrayIn1, arrayIn2, arrayIn3 ] }; + + NSString *queryString = TMDictionaryToQueryString(input); + NSDictionary *output = TMQueryStringToDictionary(queryString); + + XCTAssertTrue([key isEqualToString:output.allKeys.firstObject]); + XCTAssertEqualObjects(input, output); +} + @end