Creating URL request parameters from NSDictionary objects in ObjectiveC

With all the URL processing objects located in Cocoa standard libraries (NSURL, NSMutableURL, NSMutableURLRequest, etc.), I know that I should ignore the easy way to programmatically compose a GET request.

I am currently manually adding "?" followed by pairs of name values ​​appended by "&", but all of my name and value pairs must be manually encoded, so NSMutableURLRequest does not fail when it tries to connect to the URL.

This is similar to what I should use a preprocessed API for ... is there anything out of the box to add NSDictionary query parameters to NSURL? Is there any other way that I should approach this?

+86
objective-c nsurl
Apr 05 '09 at 5:51
source share
12 answers

Introduced in iOS8 and OS X 10.10 NSURLQueryItem , which can be used to create queries. In the NSURLQueryItem docs :

The NSURLQueryItem object is a single name / value pair for the element in the request part of the URL. You use query elements with the queryItems property of the NSURLComponents object.

To create one, use the assigned queryItemWithName:value: initializer, and then add them to NSURLComponents to generate the NSURL . For example:

 NSURLComponents *components = [NSURLComponents componentsWithString:@"http://stackoverflow.com"]; NSURLQueryItem *search = [NSURLQueryItem queryItemWithName:@"q" value:@"ios"]; NSURLQueryItem *count = [NSURLQueryItem queryItemWithName:@"count" value:@"10"]; components.queryItems = @[ search, count ]; NSURL *url = components.URL; // http://stackoverflow.com?q=ios&count=10 

Please note that the question mark and ampersand are automatically processed. Creating an NSURL from a parameter dictionary is as simple as:

 NSDictionary *queryDictionary = @{ @"q": @"ios", @"count": @"10" }; NSMutableArray *queryItems = [NSMutableArray array]; for (NSString *key in queryDictionary) { [queryItems addObject:[NSURLQueryItem queryItemWithName:key value:queryDictionary[key]]]; } components.queryItems = queryItems; 

I also wrote a blog post on how to create URLs with NSURLComponents and NSURLQueryItems .

+130
Oct 23 '14 at 23:22
source share

You can create a category for NSDictionary to do this - the standard method in the Cocoa library I could not find. The code I use is as follows:

 // file "NSDictionary+UrlEncoding.h" #import <cocoa/cocoa.h> @interface NSDictionary (UrlEncoding) -(NSString*) urlEncodedString; @end 

with this implementation:

 // file "NSDictionary+UrlEncoding.m" #import "NSDictionary+UrlEncoding.h" // helper function: get the string form of any object static NSString *toString(id object) { return [NSString stringWithFormat: @"%@", object]; } // helper function: get the url encoded string form of any object static NSString *urlEncode(id object) { NSString *string = toString(object); return [string stringByAddingPercentEscapesUsingEncoding: NSUTF8StringEncoding]; } @implementation NSDictionary (UrlEncoding) -(NSString*) urlEncodedString { NSMutableArray *parts = [NSMutableArray array]; for (id key in self) { id value = [self objectForKey: key]; NSString *part = [NSString stringWithFormat: @"%@=%@", urlEncode(key), urlEncode(value)]; [parts addObject: part]; } return [parts componentsJoinedByString: @"&"]; } @end 

I think the code is pretty simple, but I will discuss it in more detail at http://blog.ablepear.com/2008/12/urlencoding-category-for-nsdictionary.html .

+47
Apr 05 '09 at 6:44
source share

I wanted to use Chris' answer, but it was not written for Automatic Reference Counting (ARC), so I updated it. I thought I would make my decision if anyone else had the same problem. Note. replace self name of the instance or class where necessary.

 +(NSString*)urlEscapeString:(NSString *)unencodedString { CFStringRef originalStringRef = (__bridge_retained CFStringRef)unencodedString; NSString *s = (__bridge_transfer NSString *)CFURLCreateStringByAddingPercentEscapes(NULL,originalStringRef, NULL, (CFStringRef)@"!*'\"();:@&=+$,/?%#[]% ", kCFStringEncodingUTF8); CFRelease(originalStringRef); return s; } +(NSString*)addQueryStringToUrlString:(NSString *)urlString withDictionary:(NSDictionary *)dictionary { NSMutableString *urlWithQuerystring = [[NSMutableString alloc] initWithString:urlString]; for (id key in dictionary) { NSString *keyString = [key description]; NSString *valueString = [[dictionary objectForKey:key] description]; if ([urlWithQuerystring rangeOfString:@"?"].location == NSNotFound) { [urlWithQuerystring appendFormat:@"?%@=%@", [self urlEscapeString:keyString], [self urlEscapeString:valueString]]; } else { [urlWithQuerystring appendFormat:@"&%@=%@", [self urlEscapeString:keyString], [self urlEscapeString:valueString]]; } } return urlWithQuerystring; } 
+28
Dec 07 '11 at 19:04
source share

Other answers work fine if the values ​​are strings, however, if the values ​​are dictionaries or arrays, this code will handle this.

It's important to note that there is no standard way to pass an array / dictionary along a query string, but PHP handles this output just fine

 -(NSString *)serializeParams:(NSDictionary *)params { /* Convert an NSDictionary to a query string */ NSMutableArray* pairs = [NSMutableArray array]; for (NSString* key in [params keyEnumerator]) { id value = [params objectForKey:key]; if ([value isKindOfClass:[NSDictionary class]]) { for (NSString *subKey in value) { NSString* escaped_value = (NSString *)CFURLCreateStringByAddingPercentEscapes(NULL, (CFStringRef)[value objectForKey:subKey], NULL, (CFStringRef)@"!*'();:@&=+$,/?%#[]", kCFStringEncodingUTF8); [pairs addObject:[NSString stringWithFormat:@"%@[%@]=%@", key, subKey, escaped_value]]; } } else if ([value isKindOfClass:[NSArray class]]) { for (NSString *subValue in value) { NSString* escaped_value = (NSString *)CFURLCreateStringByAddingPercentEscapes(NULL, (CFStringRef)subValue, NULL, (CFStringRef)@"!*'();:@&=+$,/?%#[]", kCFStringEncodingUTF8); [pairs addObject:[NSString stringWithFormat:@"%@[]=%@", key, escaped_value]]; } } else { NSString* escaped_value = (NSString *)CFURLCreateStringByAddingPercentEscapes(NULL, (CFStringRef)[params objectForKey:key], NULL, (CFStringRef)@"!*'();:@&=+$,/?%#[]", kCFStringEncodingUTF8); [pairs addObject:[NSString stringWithFormat:@"%@=%@", key, escaped_value]]; [escaped_value release]; } } return [pairs componentsJoinedByString:@"&"]; } 

<strong> Examples

 [foo] => bar [translations] => { [one] => uno [two] => dos [three] => tres } 

Foo = bar & translations [one] = UNO & translations [two] = dos & translations [three] = Tres

 [foo] => bar [translations] => { uno dos tres } 

Foo = bar & translations [] = UNO & translations [] = dos & translations [] = Tres

+22
Oct 22 '12 at 19:16
source share

I reorganized and converted to Albeebe's ARC answer

 - (NSString *)serializeParams:(NSDictionary *)params { NSMutableArray *pairs = NSMutableArray.array; for (NSString *key in params.keyEnumerator) { id value = params[key]; if ([value isKindOfClass:[NSDictionary class]]) for (NSString *subKey in value) [pairs addObject:[NSString stringWithFormat:@"%@[%@]=%@", key, subKey, [self escapeValueForURLParameter:[value objectForKey:subKey]]]]; else if ([value isKindOfClass:[NSArray class]]) for (NSString *subValue in value) [pairs addObject:[NSString stringWithFormat:@"%@[]=%@", key, [self escapeValueForURLParameter:subValue]]]; else [pairs addObject:[NSString stringWithFormat:@"%@=%@", key, [self escapeValueForURLParameter:value]]]; } return [pairs componentsJoinedByString:@"&"]; 

}

 - (NSString *)escapeValueForURLParameter:(NSString *)valueToEscape { return (__bridge_transfer NSString *) CFURLCreateStringByAddingPercentEscapes(NULL, (__bridge CFStringRef) valueToEscape, NULL, (CFStringRef) @"!*'();:@&=+$,/?%#[]", kCFStringEncodingUTF8); } 
+8
Jun 30 '14 at 10:23
source share

If you already use AFNetworking (as was the case with me), you can use its class AFHTTPRequestSerializer to create the required NSURLRequest .

[[AFHTTPRequestSerializer serializer] requestWithMethod:@"GET" URLString:@"YOUR_URL" parameters:@{PARAMS} error:nil];

If you only need the URL of your work, use NSURLRequest.URL .

+5
Dec 24 '14 at 8:03
source share

Here is a simple example in Swift (iOS8 +):

 private let kSNStockInfoFetchRequestPath: String = "http://dev.markitondemand.com/Api/v2/Quote/json" private func SNStockInfoFetchRequestURL(symbol:String) -> NSURL? { if let components = NSURLComponents(string:kSNStockInfoFetchRequestPath) { components.queryItems = [NSURLQueryItem(name:"symbol", value:symbol)] return components.URL } return nil } 
+3
Apr 26 '15 at 10:23
source share

I have another solution:

http://splinter.com.au/build-a-url-query-string-in-obj-c-from-a-dict

 +(NSString*)urlEscape:(NSString *)unencodedString { NSString *s = (NSString *)CFURLCreateStringByAddingPercentEscapes(NULL, (CFStringRef)unencodedString, NULL, (CFStringRef)@"!*'\"();:@&=+$,/?%#[]% ", kCFStringEncodingUTF8); return [s autorelease]; // Due to the 'create rule' we own the above and must autorelease it } // Put a query string onto the end of a url +(NSString*)addQueryStringToUrl:(NSString *)url params:(NSDictionary *)params { NSMutableString *urlWithQuerystring = [[[NSMutableString alloc] initWithString:url] autorelease]; // Convert the params into a query string if (params) { for(id key in params) { NSString *sKey = [key description]; NSString *sVal = [[params objectForKey:key] description]; // Do we need to add ?k=v or &k=v ? if ([urlWithQuerystring rangeOfString:@"?"].location==NSNotFound) { [urlWithQuerystring appendFormat:@"?%@=%@", [Http urlEscape:sKey], [Http urlEscape:sVal]]; } else { [urlWithQuerystring appendFormat:@"&%@=%@", [Http urlEscape:sKey], [Http urlEscape:sVal]]; } } } return urlWithQuerystring; } 

Then you can use it like this:

 NSDictionary *params = @{@"username":@"jim", @"password":@"abc123"}; NSString *urlWithQuerystring = [self addQueryStringToUrl:@"https://myapp.com/login" params:params]; 
+2
Apr 13 2018-11-11T00:
source share
 -(NSString*)encodeDictionary:(NSDictionary*)dictionary{ NSMutableString *bodyData = [[NSMutableString alloc]init]; int i = 0; for (NSString *key in dictionary.allKeys) { i++; [bodyData appendFormat:@"%@=",key]; NSString *value = [dictionary valueForKey:key]; NSString *newString = [value stringByReplacingOccurrencesOfString:@" " withString:@"+"]; [bodyData appendString:newString]; if (i < dictionary.allKeys.count) { [bodyData appendString:@"&"]; } } return bodyData; } 
+2
Oct 11 '14 at 9:44
source share

I took Joel's recommendation to use URLQueryItems and turned into a Swift Extension (Swift 3)

 extension URL { /// Creates an NSURL with url-encoded parameters. init?(string : String, parameters : [String : String]) { guard var components = URLComponents(string: string) else { return nil } components.queryItems = parameters.map { return URLQueryItem(name: $0, value: $1) } guard let url = components.url else { return nil } // Kinda redundant, but we need to call init. self.init(string: url.absoluteString) } } 

(The self.init method is kind of crappy, but there was no NSURL init with components)

Can be used as

 URL(string: "http://www.google.com/", parameters: ["q" : "search me"]) 
+2
29 Sep '16 at 12:51
source share

Easy way to convert NSDictionary to url query string in Objective-c

Ex: first_name = Steve & middle_name = Gates & last_name = Jobs & address = Palo Alto, California

  NSDictionary *sampleDictionary = @{@"first_name" : @"Steve", @"middle_name" : @"Gates", @"last_name" : @"Jobs", @"address" : @"Palo Alto, California"}; NSMutableString *resultString = [NSMutableString string]; for (NSString* key in [sampleDictionary allKeys]){ if ([resultString length]>0) [resultString appendString:@"&"]; [resultString appendFormat:@"%@=%@", key, [sampleDictionary objectForKey:key]]; } NSLog(@"QueryString: %@", resultString); 

Hope will help :)

+2
Jun 09 '17 at 7:01
source share

Another solution, if you use RestKit , there is a function in RKURLEncodedSerialization called RKURLEncodedStringFromDictionaryWithEncoding that does exactly what you want.

+1
Mar 24 '16 at 20:37
source share



All Articles