Proper URL (Percent) Encoding in iOS

iOS SDKIf you are developing an iPhone app makes RESTful remote calls to a server in the Internet, you will likely need to encode your parameter to a percent encoded string (aka URL-encoding) so that you can properly embed the information in a URI before passing it to the remote server. For example, say you have an address that looks like this:

One Broadway, Cambridge, MA

Under URL encoding, the string becomes:

One%20Broadway,%20Cambridge,%20MA

To encode a string using URL-encodng, simply use following method in NSString:

stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding

Conversely, you can use the following method to decode a URL-encoded string to back to its original text:

stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding

Full example:

NSString *rawText = @"One Broadway, Cambridge, MA";

NSString *encodedText = [rawText stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
NSLog(@"Encoded text: %@", encodedText);
NSString *decodedText = [encodedText stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
NSLog(@"Original text: %@", decodedText);

Here’s the output:
Encoded text: One%20Broadway,%20Cambridge,%20MA
Original text: One Broadway, Cambridge, MA

But there’s just one problem, stringByAddingPercentEscapesUsingEncoding doesn’t encode reserved characters like ampersand (&) and slash (/). For example:

NSString *rawText = @"Bed Bath & Beyond - URL=http://www.bedbathandbeyond.com/";

NSString *encodedText = [rawText stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
NSLog(@"Encoded text: %@", encodedText);
NSString *decodedText = [encodedText stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
NSLog(@"Original text: %@", decodedText);

Here’s the output:

Encoded text: Bed%20Bath%20&%20Beyond%20-%20URL=http://www.bedbathandbeyond.com/
Original text: Bed Bath & Beyond – URL=http://www.bedbathandbeyond.com/

As you can see, & and / (in bold red) aren’t encoded. As a workaround, use Foundation function CFURLCreateStringByAddingPercentEscapes instead (as suggested by Simon). Full example here:

// Encode a string to embed in an URL.
NSString* encodeToPercentEscapeString(NSString *string) {
  return (NSString *)
  CFURLCreateStringByAddingPercentEscapes(NULL,
                                          (CFStringRef) string,
                                          NULL,
                                          (CFStringRef) @"!*'();:@&=+$,/?%#[]",
                                          kCFStringEncodingUTF8);
}

// Decode a percent escape encoded string.
NSString* decodeFromPercentEscapeString(NSString *string) {
  return (NSString *)
  CFURLCreateStringByReplacingPercentEscapesUsingEncoding(NULL,
                                                          (CFStringRef) string,
                                                          CFSTR(""),
                                                          kCFStringEncodingUTF8);
}

int main (int argc, const char * argv[]) {
  NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];

  NSString *rawText = @"Bed Bath & Beyond - URL=http://www.bedbathandbeyond.com/";

  NSString *encodedText = encodeToPercentEscapeString(rawText);
  NSLog(@"Encoded text: %@", encodedText);
  NSString *decodedText = decodeFromPercentEscapeString(encodedText);
  NSLog(@"Original text: %@", decodedText);

  [pool drain];
  return 0;
}

And the output:

Encoded text: Bed%20Bath%20%26%20Beyond%20-%20URL%3Dhttp%3A%2F%2Fwww.bedbathandbeyond.com%2F
Original text: Bed Bath & Beyond – URL=http://www.bedbathandbeyond.com/

This time it correct encode the & and / characters. Of course, you won’t be encoding the entire URL but only the URI parameters. There’s no code download for this, simply cut the above code snippets to your code and paste to your code base.

Errata (Feb 18, 2011)

CFURLCreateStringByAddingPercentEscapes returns a string reference with a retain count of +1. So remember to release it when you are done using it.

  • clozach

     Thank you! This was exactly what I needed.