Question

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!

Was it helpful?

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 }

OTHER TIPS

You may want to read the documentation for -[NSString initWithFormat:arguments:]. That method accepts a va_list parameter and will probably do what you want.

The reason your sample code doesn't work is because stringWithFormat needs a separate argument for each placeholder that appears in the format string. Your format string looks like it contains three %@ placeholders, but you're only passing one argument, listOfAttributes.

The format within stringPath is specifying that there should be 3 arguments, but you are only supplying one - listOfAttributes.

listOfAttributes is one argument not 3.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top