diff options
-rw-r--r-- | Foundation/GTMNSDictionary+URLArguments.h | 8 | ||||
-rw-r--r-- | Foundation/GTMNSDictionary+URLArguments.m | 31 | ||||
-rw-r--r-- | Foundation/GTMNSDictionary+URLArgumentsTest.m | 33 | ||||
-rw-r--r-- | ReleaseNotes.txt | 2 |
4 files changed, 73 insertions, 1 deletions
diff --git a/Foundation/GTMNSDictionary+URLArguments.h b/Foundation/GTMNSDictionary+URLArguments.h index 4bc896d..b094411 100644 --- a/Foundation/GTMNSDictionary+URLArguments.h +++ b/Foundation/GTMNSDictionary+URLArguments.h @@ -21,8 +21,14 @@ /// Utility for building a URL or POST argument string. @interface NSDictionary (GTMNSDictionaryURLArgumentsAdditions) +/// Returns a dictionary of the decoded key-value pairs in a http arguments +/// string of the form key1=value1&key2=value2&...&keyN=valueN. +/// Keys and values will be unescaped automatically. +/// Only the first value for a repeated key is returned. ++ (NSDictionary *)gtm_dictionaryWithHttpArgumentsString:(NSString *)argString; + /// Gets a string representation of the dictionary in the form -/// key1=value1&key2&value2&...&keyN=valueN, suitable for use as either +/// key1=value1&key2=value2&...&keyN=valueN, suitable for use as either /// URL arguments (after a '?') or POST body. Keys and values will be escaped /// automatically, so should be unescaped in the dictionary. - (NSString *)gtm_httpArgumentsString; diff --git a/Foundation/GTMNSDictionary+URLArguments.m b/Foundation/GTMNSDictionary+URLArguments.m index 89610e4..4799b2d 100644 --- a/Foundation/GTMNSDictionary+URLArguments.m +++ b/Foundation/GTMNSDictionary+URLArguments.m @@ -24,6 +24,37 @@ @implementation NSDictionary (GTMNSDictionaryURLArgumentsAdditions) GTM_METHOD_CHECK(NSString, gtm_stringByEscapingForURLArgument); +GTM_METHOD_CHECK(NSString, gtm_stringByUnescapingFromURLArgument); + ++ (NSDictionary *)gtm_dictionaryWithHttpArgumentsString:(NSString *)argString { + NSMutableDictionary* ret = [NSMutableDictionary dictionary]; + NSArray* components = [argString componentsSeparatedByString:@"&"]; + NSString* component; + // Use reverse order so that the first occurrence of a key replaces + // those subsequent. + GTM_FOREACH_ENUMEREE(component, [components reverseObjectEnumerator]) { + if ([component length] == 0) + continue; + NSRange pos = [component rangeOfString:@"="]; + NSString *key; + NSString *val; + if (pos.location == NSNotFound) { + key = [component gtm_stringByUnescapingFromURLArgument]; + val = @""; + } else { + key = [[component substringToIndex:pos.location] + gtm_stringByUnescapingFromURLArgument]; + val = [[component substringFromIndex:pos.location + pos.length] + gtm_stringByUnescapingFromURLArgument]; + } + // gtm_stringByUnescapingFromURLArgument returns nil on invalid UTF8 + // and NSMutableDictionary raises an exception when passed nil values. + if (!key) key = @""; + if (!val) val = @""; + [ret setObject:val forKey:key]; + } + return ret; +} - (NSString *)gtm_httpArgumentsString { NSMutableArray* arguments = [NSMutableArray arrayWithCapacity:[self count]]; diff --git a/Foundation/GTMNSDictionary+URLArgumentsTest.m b/Foundation/GTMNSDictionary+URLArgumentsTest.m index f01519e..8ec9520 100644 --- a/Foundation/GTMNSDictionary+URLArgumentsTest.m +++ b/Foundation/GTMNSDictionary+URLArgumentsTest.m @@ -25,6 +25,39 @@ @implementation GTMNSDictionary_URLArgumentsTest +- (void)testFromArgumentsString { + STAssertEqualObjects([NSDictionary gtm_dictionaryWithHttpArgumentsString:@""], + [NSDictionary dictionary], + @"- empty arguments string should give an empty dictionary"); + STAssertEqualObjects([NSDictionary gtm_dictionaryWithHttpArgumentsString:@"a"], + [NSDictionary dictionaryWithObject:@"" forKey:@"a"], + @"- missing '=' should result in an empty string value"); + STAssertEqualObjects([NSDictionary gtm_dictionaryWithHttpArgumentsString:@"a="], + [NSDictionary dictionaryWithObject:@"" forKey:@"a"], + @"- no value"); + STAssertEqualObjects([NSDictionary gtm_dictionaryWithHttpArgumentsString:@"&a=1"], + [NSDictionary dictionaryWithObject:@"1" forKey:@"a"], + @"- empty segment should be skipped"); + STAssertEqualObjects([NSDictionary gtm_dictionaryWithHttpArgumentsString:@"abc=123"], + [NSDictionary dictionaryWithObject:@"123" forKey:@"abc"], + @"- simple one-pair dictionary should work"); + STAssertEqualObjects([NSDictionary gtm_dictionaryWithHttpArgumentsString:@"a=1&a=2&a=3"], + [NSDictionary dictionaryWithObject:@"1" forKey:@"a"], + @"- only first occurrence of a key is returned"); + NSString* complex = @"a%2Bb=specialkey&complex=1%2B1%21%3D3%20%26%202%2A6%2F3%3D4&c"; + NSDictionary* result = [NSDictionary dictionaryWithObjectsAndKeys: + @"1+1!=3 & 2*6/3=4", @"complex", + @"specialkey", @"a+b", + @"", @"c", + nil]; + STAssertEqualObjects([NSDictionary gtm_dictionaryWithHttpArgumentsString:complex], + result, + @"- keys and values should be unescaped correctly"); + STAssertEqualObjects([NSDictionary gtm_dictionaryWithHttpArgumentsString:@"a=%FC"], + [NSDictionary dictionaryWithObject:@"" forKey:@"a"], + @"- invalid UTF8 characters result in an empty value, not a crash"); +} + - (void)testArgumentsString { STAssertEqualObjects([[NSDictionary dictionary] gtm_httpArgumentsString], @"", @"- empty dictionary should give an empty string"); diff --git a/ReleaseNotes.txt b/ReleaseNotes.txt index d2b55ea..aa7083a 100644 --- a/ReleaseNotes.txt +++ b/ReleaseNotes.txt @@ -314,6 +314,8 @@ Changes since 1.5.1 - Added NSMatrix and NSCell to GTMLocalizer support. +- Added gtm_dictionaryWithHttpArgumentsString to NSDictionary+URLArguments. + Release 1.5.1 Changes since 1.5.0 |