iOS Dev: Decoding PHP URL Encoded Strings with NSString

If you do any PHP development, you should be familiar with the urlencode() function:

1
2
3
4
5
6
7
8
9
10
<?php

     $string = "This string will totally screw things up if it isn't urlencoded!";
     $encoded_string = urlencode($string);
     echo $encoded_string;

     // Result:
     // This+string+will+totally+screw+things+up+if+it+isn%27t+urlencoded%21

?>

Notice that the resulting string contains percent escapes in place of the apostrophe (‘%27′) and exclamation point (‘%21′), but the spaces have been replaced with ‘+’ symbols. This is what urlencode() does, and it works just fine if the encoded string will be decoded by the urldecode() function in PHP.

Things get a little more complicated if you use urlencode() to encode a query response for an iOS app. The standard, easy way to remove percent escapes is the NSString stringByReplacingPercentEscapesUsingEncoding: method. When used on the encoded string shown above, we get this:

1
2
3
4
5
6
NSString *encodedString = @"This+string+will+totally+screw+things+up+if+it+isn%27t+urlencoded%21"; // Presumably returned from a server call.
NSString *decodedString = [encodedString stringByReplacingPercentEscapesUsingEncoding: NSUTF8StringEncoding];
NSLog(@"%@", decodedString);

// Result:
// This+string+will+totally+screw+things+up+if+it+isn't+urlencoded!

Oh crap. Why are the ‘+’ symbols still there?

Well, it turns out that stringByReplacingPercentEscapesUsingEncoding: is named accurately – it only replaces percent escapes, so it thinks those ‘+’ symbols are a valid part of the string. There are two simple ways around this problem.

If you have to work with an existing web service that uses urlencode(), you can prepare the string for decoding with the NSString stringByReplacingOccurrencesOfString:withString: method, which replaces the ‘+’ symbols with percent escapes representing spaces (%20):

1
2
3
4
5
6
7
8
9
10
11
12
13
NSString *encodedString = @"This+string+will+totally+screw+things+up+if+it+isn%27t+urlencoded%21";

NSString *preparedString = [encodedString stringByReplacingOccurrencesOfString: @"+" withString: @"%20"];
NSLog(@"%@", preparedString);

// Result:
// This%20string%20will%20totally%20screw%20things%20up%20if%20it isn%27t%20urlencoded%21

NSString *decodedString = [preparedString stringByReplacingPercentEscapesUsingEncoding: NSUTF8StringEncoding];
NSLog(@"%@", decodedString);

// Result:
// This string will totally screw things up if it isn't urlencoded!

Ahhhhh… much better!

VERY IMPORTANT DETAIL: You have to replace the ‘+’ symbols before decoding the percent escapes or you’ll run into problems when you encounter a string that contains a valid ‘+’ symbol (i.e. one that the string contained before encoding). The urlencode() function escapes ‘+’ with ‘%2B’ before escaping spaces with ‘+’ , so valid ‘+’ symbols will be safe until you finally decode the string. If that seems confusing to you, read it again and again and again. Or just urlencode() a string that contains a ‘+’ symbol and study the result. You’ll figure it out.

On the other hand, if you’re building your own web service for your app, you can avoid that extra step by using PHP’s rawurlencode() function:

1
2
3
4
5
6
7
8
9
10
<?php

     $string = "This string will totally screw things up if it isn't urlencoded!";
     $encoded_string = rawurlencode($string);
     echo $encoded_string;

     // Result:
     // This%20string%20will%20totally%20screw%20things%20up%20if%20it%20isn%27t%20urlencoded%21

?>

The rawurlencode() function conforms to RFC 1738 and uses ‘%20′ escapes for spaces, so it plays nice with stringByReplacingPercentEscapesUsingEncoding: right away:

1
2
3
4
5
6
NSString *encodedString = @"This%20string%20will%20totally%20screw%20things%20up%20if%20it isn%27t%20urlencoded%21";
NSString *decodedString = [encodedString stringByReplacingPercentEscapesUsingEncoding: NSUTF8StringEncoding];
NSLog(@"%@", decodedString);

// Result:
// This string will totally screw things up if it isn't urlencoded!

There you have it! URL encoded strings decoded by NSString in one (or two) easy step(s).

Your Comments