iosobjective-cnsstringvariadic-functionsstringwithformat

Building an NSString based on a variadic number of arguments


My function takes a dictionary argument and a variadic number of NSString variables. All this combined is put in an [NSString stringWithFormat:] method, and is returned as a NSURLRequest. The method looks like this:

- (NSURLRequest *)buildPath:(NSString *)stringPath attributes:(NSString *)attribute, ...
{
    va_list list;
    NSString *eachObject;
    NSMutableArray *args = [NSMutableArray array];

    [args addObject:attribute];
    va_start(list, attribute);
    while ((eachObject = va_arg(list, NSString *))) {
        [args addObject:eachObject];
    }
    va_end(list);

    NSString *listOfAttributes = [args componentsJoinedByString:@", "];
    NSString *pathURL = _requestString[stringPath];
    NSString *path = [NSString stringWithFormat:pathURL, listOfAttributes];

    NSURL *url = [NSURL URLWithString:path];
    NSURLRequest *request = [NSURLRequest requestWithURL:url];

    return request;
}

This is what it looks like when I call the method:

NSURLRequest *request = [_venueService buildPath:@"categories"
                                          attributes:_venueService.clientID, _venueService.clientSecret, _venueService.todaysDate, nil];

When I run the program, it crashes. When I log out listOfAttributes it gives me:

client_id, client_secret, 20140507

This is my 3 arguments, which is correct, and the stringPath (when I actually call it in my program I write stringPath[@"categories"]) which, when I NSLog gives me:

https://api.foursquare.com/v2/venues/categories?client_id=%@&client_secret=%@&v=%@

So, my question is, why would these two strings, combined in an [NSString stringWithFormat:] cause problems?

Any help would be greatly appreciated!


Solution

  • As Justin pointed out, there is a much simpler way of doing this. NSString has a -initWithFormat:arguments: method that does exactly what you want.

    Also, your method name has a few issues:

    1. Naming convention - you should indicate in the method name its purpose (creating a URL request)
    2. You are passing in an (NSDictionary *) for the path, but casting it to an (NSString *) when you use it. The two objects are not type compatible. I'm supposing this might be a typo when you copy-pasted your code?
    3. Might as well use the same calling convention as NSString's +stringWithFormat: method.

    Given all of the above, the method becomes something like (without error checking):

    - (NSURLRequest  *)URLRequestWithFormat:(NSString *)format, ... {
        va_list arguments;
        va_start(arguments, format);
        NSString *urlPath = [[NSString alloc] initWithFormat:format arguments:arguments];
        va_end(arguments);
        NSURL *url = [NSURL URLWithString:urlPath];
        NSURLRequest *request = [NSURLRequest requestWithURL:url];
        return request;
    }
    

    This worked fine with a call like:

    NSURLRequest *request = [self URLRequestWithFormat:@"https://api.foursquare.com/v2/venues/categories/client_id=%@&client_secret=%@&v=%@", @"One",@"Two",@"Three"];
    NSLog(@"Request: %@", request);
    

    With output:

    2014-05-07 09:52:30.645 Test[5888:60b] Request: <NSURLRequest: 0x8c64f30> { URL: https://api.foursquare.com/v2/venues/categories/client_id=One&client_secret=Two&v=Three }