12

I'm trying to URL encode a string to form a GET request from objective-c.

NSString *params = @"'Decoded data!'/foo.bar:baz";

NSRunAlertPanel( @"Error", [params urlEncoded], @"OK", nil, nil );

This is the category extending NSString

    -(NSString *) urlEncoded
{
    NSString *encoded = (NSString *)CFURLCreateStringByAddingPercentEscapes(
                                                   NULL,
                                                   (CFStringRef)self,
                                                   NULL,
                                                   (CFStringRef)@"!*'\"();:@&=+$,/?%#[]% ",
                                                   kCFStringEncodingUTF8 );
    return encoded;
}

So the first time I run it I get back

1606410046ecoded          1606410784ata2270.000000foo.bar0X1.001716P-1042baz

from the dialog box.

Immediately after I run it again I get this

1606410046ecoded          1606410944ata227369374562920703448982951250259562309742470533728899744288431318481119278377104028261651081181287077973859930826299575521579020410425419424562236383226511593137467590082636817579938932512039895040.000000foo.bar0X1.66E6156303225P+771baz

Then if I run it AGAIN it goes back to the first one. It's really weird.

If params is set to @"&" or @" " I just get back a "2" (w/o the quotes) in the dialog box.

Also is there a way I can have the % signs be shown in the alert dialog?

Thanks

nevan king
  • 112,709
  • 45
  • 203
  • 241
Chris
  • 988
  • 3
  • 18
  • 30

5 Answers5

23

I think the NSAlert is interpreting the % characters as string format specifiers which are being filled with random data. Just NSLog the output and it's fine:

%27Decoded%20data%21%27%2Ffoo.bar%3Abaz

Also, you have a memory leak in your -urlEncoded category method. You create the string using a CF function containing Create so you are responsible for releasing it.

-(NSString *) urlEncoded
{
   CFStringRef urlString = CFURLCreateStringByAddingPercentEscapes(
                                                   NULL,
                                                   (CFStringRef)self,
                                                   NULL,
                                                   (CFStringRef)@"!*'\"();:@&=+$,/?%#[]% ",
                                                   kCFStringEncodingUTF8 );
    return [(NSString *)urlString autorelease];
}
Rob Keniger
  • 45,830
  • 6
  • 101
  • 134
  • Thanks for the memory leak warning. Actually the problem persists with NSLog too. I fixed it by replacing % with %% in the string detailed in my response below. – Chris Apr 07 '10 at 19:41
  • 3
    That means you're probably using `NSLog` like this: `NSLog(encoded)`. The first argument to `NSLog` is a format string, not a plain string. Any string format placeholders will be replaced with the arguments which follow, which in that case are undefined. You must log the string like this: `NSLog(@"%@",encoded);`. – Rob Keniger Apr 08 '10 at 00:49
  • 1
    Using `return [NSMakeCollectable(urlString) autorelease];` also makes it GC-clean, and avoids the cast. – Quinn Taylor Oct 18 '11 at 00:22
6

I've open sourced my URL encoder utility class which intelligently skips the domain and path portion of the URL (to avoid encoding the slashes, etc...) and escape only the percent sequences that are not followed by 2-digit hex codes (to prevent double encoding of percents like this: %20 -> %2520).

It has been tested against over 10,000 URLs and is very robust and performant.

You can learn more about (and download) my implementation here... http://jayfuerstenberg.com/devblog/url-encoding-in-objective-c

Jason Fuerstenberg
  • 1,341
  • 13
  • 13
2

Rather than autorelease, which is no longer available when using ARC, create your instance method by passing a string and using CFBridgingRelease:

- (NSString *)urlEncodeWithString: (NSString*)string
{
  CFStringRef urlString = CFURLCreateStringByAddingPercentEscapes(
                                                                  NULL,
                                                                  (CFStringRef)string,
                                                                  NULL,
                                                                  (CFStringRef)@"!*'\"();:@&=+$,/?%#[]% ",
                                                                  kCFStringEncodingUTF8 );
  return (NSString *)CFBridgingRelease(urlString);
}
Pat
  • 303
  • 2
  • 5
  • 11
1

OK turns out it was a nonissue. It was being encoded correctly because I checked the server logs and it seems the request params were encoded.

And for displaying the encoded string in the dialog box correctly, I just replaced all instances of % with %% after the fact.

Chris
  • 988
  • 3
  • 18
  • 30
0

In my opinion the easiest way is to use convenient method supplied with NSString (NSURLUtilities) category

My implementation:

- (NSString *) urlEncodedString
{
    NSString *result = [self stringByReplacingOccurrencesOfString:@" " withString:@"+"];
    result = [result stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
    return result;
}
  • Doesn't work. Translates "This is my + statement. Got it?" to "This+is+my+++statement.+Got+it?" instead of "This+is+my+%2B+statement.+Got+it%3F". – Volomike Jul 15 '16 at 06:57