If you have tried to send any information using a GET web request, you would have come across an annoying problem. That annoying problem is making sure that the URL is correctly encoded.

At first glance it would seem that the Cocoa Frameworks do this for you, and you would be right …. well kind of.

The issue is that by default most of these methods leave characters such as & = ? within a URL, as they are strictly speaking valid. The problem is that these characters have special meanings in a GET request, and will more than likely make your request in valid.

Luckily there is a function in Core Foundation that helps:

1234567
CFStringRef CFURLCreateStringByAddingPercentEscapes (   CFAllocatorRef allocator,   CFStringRef originalString,   CFStringRef charactersToLeaveUnescaped,   CFStringRef legalURLCharactersToBeEscaped,   CFStringEncoding encoding);

What makes this function useful, is the legalURLCharactersToBeEscaped parameter. This will escape legal characters such as & ? = if they are supplied. This allows you to escape parameters using the following code.

12
 CFURLCreateStringByAddingPercentEscapes(kCFAllocatorDefault, (CFStringRef)parameter, NULL, CFSTR(":/?#[]@!$&’()*+,;="), kCFStringEncodingUTF8)

An example of when to use this, is Twitters Update status API. You can find that here http://apiwiki.twitter.com/Twitter-REST-API-Method%3A-statuses%C2%A0update

To update your status to the following:

This is my status

You would need to post up the following URL:

http://twitter.com/statuses/update.xml?status=This%20is%20my%20status

As this is such a common problem of mine, I have created a category on NSURL. This allows you to pass in a base URL and a parameters dictionary.

123456789101112131415161718192021222324252627282930313233343536373839404142434445
 + (NSURL *)URLWithBaseString:(NSString *)baseString parameters:(NSDictionary *)parameters{ NSMutableString *urlString = [NSMutableString string]//The URL starts with the base string[urlString appendString:baseString]NSString *escapedString; NSInteger keyIndex = 0for (id key in parameters) { //First Parameter needs to be prefixed with a ? and any other parameter needs to be prefixed with an &if(keyIndex ==0){escapedString = (NSString*)CFURLCreateStringByAddingPercentEscapes(kCFAllocatorDefault, (CFStringRef)[parameters valueForKey:key], NULL, CFSTR(":/?#[]@!$&’()*+,;="), kCFStringEncodingUTF8)[urlString appendFormat:@"?%@=%@",key,escapedString];[escapedString release];}else{ escapedString = (NSString*)CFURLCreateStringByAddingPercentEscapes(kCFAllocatorDefault, (CFStringRef)[parameters valueForKey:key], NULL, CFSTR(":/?#[]@!$&’()*+,;="), kCFStringEncodingUTF8)[urlString appendFormat:@"&%@=%@",key,escapedString];[escapedString release]} keyIndex++;} return [NSURL URLWithString:urlString]}

Using a parameters dictionary keeps the code nice and clean, but beware, to use the category method above you still have to make sure that your keys, and the base URL are correctly encoded (no spaces or invalid characters !!!!!).

As we now have a category method to do all the hard work for us, to create the Twitter URL you just need to do the following:

12345
NSString *baseString=@"http://twitter.com/statuses/update.xml"NSDictionary *dictionary=[NSDictionary dictionaryWithObjectsAndKeys:@"This is my status",@"status",nil]NSURL *url=[NSURL URLWithBaseString:baseString parameters:dictionary];

And thats it. Obviously this category can be used for things other than twitter ….. if you really want to.