URL encoding an NSString on iOS
As an example, if you want to use the string hell & brimstone + earthly/delight (and why wouldn’t you?) as a parameter to a URL then you’ll need to convert it to hell+%26+brimstone+%2B+earthly%2Fdelight so that the ampersand becomes %26, the plus becomes %2B and the slash becomes %2F. Note that spaces are encoded as pluses, which is why the original plus sign needs to be encoded.
But stringByAddingPercentEscapesUsingEncoding doesn’t respect these rules, and actually produces this: hell%20&%20brimstone%20+%20earthly/delight
It fails to encode the ampersand, the slash and the plus, and many (most?) web servers will be confused by that. It encodes the spaces as %20, which is just as acceptable as encoding them as a plus.
In short, it’s completely broken, which is frustrating. But thankfully there’s a lower-level API we can use which, thanks to the magic of Objective-C categories, we can tack on to NSString. This is based on a blogpost by Simon Woodside, which I’ve just turned into a category.
Here’s the category’s header file:
#import <Foundation/Foundation.h>
@interface NSString (URLEncoding)
-(NSString *)urlEncodeUsingEncoding:(NSStringEncoding)encoding;
@end
And here’s the implementation:
#import "NSString+URLEncoding.h"
@implementation NSString (URLEncoding)
-(NSString *)urlEncodeUsingEncoding:(NSStringEncoding)encoding {
return (NSString *)CFURLCreateStringByAddingPercentEscapes(NULL,
(CFStringRef)self,
NULL,
(CFStringRef)@"!*'\"();:@&=+$,/?%#[]% ",
CFStringConvertNSStringEncodingToEncoding(encoding));
}
@end
And now we can simply do this:
NSString *raw = @"hell & brimstone + earthly/delight";
NSString *url = [NSString stringWithFormat:@"http://example.com/example?param=%@",
[raw urlEncodeUsingEncoding:NSUTF8Encoding]];
NSLog(url);
And the result will be exactly as we hoped:
http://example.com/example?param=hell%20%26%20brimstone%20%2B%20earthly%2Fdelight