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.
You might want to look at pathComponents
which returns an array of the components of the URL. Get more information here.
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
Sam Soffes created a well maintained Category for NSURL / NSDictionary. It can be found here: https://github.com/samsoffes/sstoolkit/
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);
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
.
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.