Parse NSURL path and query (iphoneOS)

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"}

  • Using iOS pan gesture to highlight buttons in grid
  • Swift 3 UISwitch in TableViewCell loses State when scrolling
  • How to turn on/off airplane mode in IOS 5.1 using private API
  • NSString to const char * convert with Greek characters
  • What class to inherit from for bound table source
  • Should IBOutlets be ivars or properties?
  • 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…

    EDIT – This is for iphoneOS! Apparently NSURL has different functions on Mac OS

    6 Solutions Collect From Internet About “Parse NSURL path and query (iphoneOS)”

    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.