12

Are there any standard objects or functions to parse an NSURL's components? Clearly I could write one, but why re-invent the wheel?

[NSURL path] will return an NSString like "argX=x&argY=y&argZ=z" What I would rather get back is a dictionary populated with {@"argX" => @"x", @"argY" => @"y", @"argZ" = @"z"}

For the path, which returns a string like "/partA/partB/partC", I would rather get an array with the structure {[0] => @"partA", [1] => @"partB", [2] => @"partC"}

I realize this is a pretty specific ask, but it seems like something a lot of people would want.

This is for iOS! Apparently NSURL has different functions on macOS.

4
  • Good question. Do you mean: If the URL is "/partA/partB/partC", you want "0 => partA, 1=> partB, 2=>partC". If the URL is "/foo?partA=A&partB=B" you want "partA => A, partB => B". What if the URL is "/partA/partB/partC?foo=bar&ooga=booga"? In one example, the important information needed to identify the entity in question is being passed in a query string... in the other example, the identifying information is passed rest-style in the path itself. Commented Dec 27, 2009 at 23:53
  • I mean I want to parse the path and the query separately. It should not be one function that combines those two tasks. See the code I posted below for clarification. If someone answers with an existing implementation, I'll be happy to still accept that. Commented Dec 28, 2009 at 0:04
  • One thing to keep in mind is that this is a perfectly valid query string: "x=1&x=0&x=foo", so an NSDictionary might not be the best representation (unless you're using arrays as the values). Commented Mar 15, 2011 at 16:38
  • @n8gray - You're right, my answer below doesn't deal with duplicate values. I would avoid duplicate values like the plague in any real-world scenarios though, because many servers (PHP servers for example) will end up with only one value for x... x=>"foo", just like my script. PHP will however, build an array out of "x[]=1&x[]=foo", which my answer will not do. It should be a trivial exercise to expand my example to build arrays if the left hand parameter name ends in "[]" though, so I'll leave that for anyone who needs it to add. Thanks! Commented Mar 16, 2011 at 21:11

6 Answers 6

12

Alright, I got antsy and wrote a solution for extending NSString through Categories. I haven't tested this yet, but if you want to use it, go for it.

@interface NSString (ParseCategory) - (NSMutableDictionary *)explodeToDictionaryInnerGlue:(NSString *)innerGlue outterGlue:(NSString *)outterGlue; @end @implementation NSString (ParseCategory) - (NSMutableDictionary *)explodeToDictionaryInnerGlue:(NSString *)innerGlue outterGlue:(NSString *)outterGlue { // Explode based on outter glue NSArray *firstExplode = [self componentsSeparatedByString:outterGlue]; NSArray *secondExplode; // Explode based on inner glue NSInteger count = [firstExplode count]; NSMutableDictionary *returnDictionary = [NSMutableDictionary dictionaryWithCapacity:count]; for (NSInteger i = 0; i < count; i++) { secondExplode = [(NSString *)[firstExplode objectAtIndex:i] componentsSeparatedByString:innerGlue]; if ([secondExplode count] == 2) { [returnDictionary setObject:[secondExplode objectAtIndex:1] forKey:[secondExplode objectAtIndex:0]]; } } return returnDictionary; } @end 

It's called like this:

NSMutableDictionary *parsedQuery = [[myNSURL query] explodeToDictionaryInnerGlue:@"=" outterGlue=@"&"] 

For parsing the path portion of the NSURL (ie @"/partA/partB/partC"), just call this:

NSArray *parsedPath = [[nyNSURL path] componentsSeperatedByString:@"/"]; 

Be aware that parsedPath[0] will be an empty string because of the leading /!

EDIT - Here is a Category extension to NSURL for your usage pleasure. It strips the initial "/" so you don't have an empty 0 index.

@implementation NSURL (ParseCategory) - (NSArray *)pathArray { // Create a character set for the slash character NSRange slashRange; slashRange.location = (unsigned int)'/'; slashRange.length = 1; NSCharacterSet *slashSet = [NSCharacterSet characterSetWithRange:slashRange]; // Get path with leading (and trailing) slashes removed NSString *path = [[self path] stringByTrimmingCharactersInSet:slashSet]; return [path componentsSeparatedByCharactersInSet:slashSet]; } - (NSDictionary *)queryDictionary { NSDictionary *returnDictionary = [[[[self query] explodeToDictionaryInnerGlue:@"=" outterGlue:@"&"] copy] autorelease]; return returnDictionary; } @end 
Sign up to request clarification or add additional context in comments.

3 Comments

Not sure why someone down-voted this... that'll teach me to share code I guess? I wouldn't really mind if you'd left a comment, but I'll just assume you're a random troll. Hi troll.
Thanks, this works fine for me. Note that you may need to URL-decode your individual query string values as well. I am using the following code for this, first to take care of percent-escapes, then to replace "+" with space, which the first one unfortunately doesn't do: [[value stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding] stringByReplacingOccurrencesOfString:@"+" withString:@" "]
Very welcome. And yes, I left matters of encoding/decoding out of these functions.
11

Sam Soffes created a well maintained Category for NSURL / NSDictionary. It can be found here: https://github.com/samsoffes/sstoolkit/

Comments

10

You might want to look at pathComponents which returns an array of the components of the URL. Get more information here.

7 Comments

I don't believe this is available on the iphone version of the SDK. I should have clarified that I'm working on iphone OS.
[NSString pathComponents] is available in both the Cocoa and Cocoa Touch APIs. On the iPhone, you can imitate OS X 10.6's [NSURL pathComponents] with [[NSURL path] pathComponents].
Ah perfect, that's what I was looking for, for the path portion at least!
Apple docs for [NSString pathComponents] state: "Note that this method only works with file paths (not, for example, string representations of URLs)."
@MikeAbdullah: Ah, ok. For anyone else stumbling on this 3-year old question: As of iOS4 you no longer need to convert the URL to a string and can just use: -[NSURL pathComponents] directly.
|
5

Heres what i use to parse the query string

// Parse the individual parameters // parameters = @"hello=world&foo=bar"; NSMutableDictionary *dictParameters = [[NSMutableDictionary alloc] init]; NSArray *arrParameters = [parameters componentsSeparatedByString:@"&"]; for (int i = 0; i < [arrParameters count]; i++) { NSArray *arrKeyValue = [[arrParameters objectAtIndex:i] componentsSeparatedByString:@"="]; if ([arrKeyValue count] >= 2) { NSMutableString *strKey = [NSMutableString stringWithCapacity:0]; [strKey setString:[[[arrKeyValue objectAtIndex:0] lowercaseString] stringByReplacingPercentEscapesUsingEncoding: NSUTF8StringEncoding]]; NSMutableString *strValue = [NSMutableString stringWithCapacity:0]; [strValue setString:[[[arrKeyValue objectAtIndex:1] stringByReplacingOccurrencesOfString:@"+" withString:@" "] stringByReplacingPercentEscapesUsingEncoding: NSUTF8StringEncoding]]; if (strKey.length > 0) [dictParameters setObject:strValue forKey:strKey]; } } NSLog(@"Parameters: %@", dictParameters); 

Comments

2

If you do decide to write one (I'm not sure there are existing methods of getting the components you want), you might want to use NSString's componentsSeparatedByString.

Comments

2

Introduced in iOS 8 and OS X 10.10 is NSURLQueryItem, which can be used to build queries. From the docs on NSURLQueryItem:

An NSURLQueryItem object represents a single name/value pair for an item in the query portion of a URL. You use query items with the queryItems property of an NSURLComponents object.

You can retrieve the query items from a URL by first creating NSURLComponents:

NSURL *url = [NSURL URLWithString:@"http://stackoverflow.com?q=ios&count=10"]; NSURLComponents *components = [NSURLComponents componentsWithURL:url resolvingAgainstBaseURL:YES]; for (NSURLQueryItem *item in components.queryItems) { NSLog(@"name: %@, value: %@", item.name, item.value); } // name: q, value: ios // name: count, value: 10 

Note that they return value for -queryItems is an array, not a dictionary. This is because the following is a valid URL. Note the two identical "keys", foo.

http://google.com?foo=bar&foo=baz

To create a URL via query items, use the designated initializer queryItemWithName:value: and then add them to NSURLComponents to generate an NSURL. For example:

NSString *urlString = @"http://stackoverflow.com"; NSURLComponents *components = [NSURLComponents componentsWithString:urlString]; 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 

Notice that the question mark and ampersand are automatically handled. Creating an NSURL from a dictionary of parameters is as simple as:

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

I've also written a blog post with more details, Building NSURLs with NSURLQueryItems.

1 Comment

Cool! Just a note to devs--if you use duplicate GET parameters you're in for a world of hurt. There is no defined spec for how to handle those, and different languages/platforms make up their own rules. See stackoverflow.com/questions/1746507/…

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.