Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Encoding spaces in UITextView / UITextField to URL format

I'm trying to send the contents of UITextView or UITextField as parameters to a php file

NSString *urlstr = [[NSString alloc] initWithFormat:@"http://server.com/file.php?name=%@&tags=%@&entry=%@",nameField.text, tagsField.text, dreamEntry.text];

When i log urlstr, the url format is ok just as long as the UITextView or UITextField don't contain spaces. How would i go about converting the spaces to %20 ?

edit

here is the code at present, which not only crashes but isn't encoding the url properly.

name=John Doe&tags=recurring nightmare&entry=Testing testing testing

is converted to

name=John -1844684964oe&tags=recurringightmare&entry=Testing 4.214929e-307sting -1.992836e+00sting

- (IBAction)sendButtonPressed:(id)sender
{

    NSString *urlString = [[NSString alloc] initWithFormat:@"http://server.com/file.php?name=%@&tags=%@&entry=%@", nameField.text, tagsField.text, dreamEntry.text];

    NSString *encodedString = [urlString stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding];

    NSURL *url = [[NSURL alloc] initWithString:encodedString];

    NSLog(encodedString);

    NSLog(urlString);

    [urlString release];
    [url release];
    [encodedString release];


}
like image 803
bcsantos Avatar asked May 27 '09 18:05

bcsantos


2 Answers

Actually, all of the previous answers contain at least some inaccuracies, which for many common values of user provided text in the TextFields would not correctly communicate with the server

stringByAddingPercentEscapesUsingEncoding: percent escapes all characters which are not valid URL characters. This method should applied once to the entire URL.

A previous answer claims that stringByAddingPercentEscapesUsingEncoding: works like the URL building classes in many scripting languages, where you should not apply it to the entire URL string, but it doesn't. Anyone can easily verify this by checking its output for unescaped &s and ?s. So it is fine to apply to the entire string, but it is not enough to apply to your 'dynamic' url content.

The previous answer is right in that you have to do some more work to the names and values that go into your CGI query string. Since CGI is specified by RFC3875, this is often referred to as RFC3875 percent escaping. It makes sure that your names and values don't contain characters that are valid URL characters but which are significant in other parts of the URL (;, ?, :, @, &, =, $, +, {, }, <, >, and ,)

However, it is very important to also finish by doing plain URL percent escapes on the full string to make sure that all characters in the string are valid URL characters. While you don't in your example, in general there could be characters in a 'static' part of the string which are not valid URL characters, so you do need to escape those as well.

Unfortunately, NSString doesn't give us the power to escape the RFC3875 significant characters so we have to dip down into CFString to do so. Obviously using CFString is a pain so I generally add a Category onto NSString like so:

@interface NSString (RFC3875)
- (NSString *)stringByAddingRFC3875PercentEscapesUsingEncoding:(NSStringEncoding)encoding;
@end

@implementation NSString (RFC3875)
- (NSString *)stringByAddingRFC3875PercentEscapesUsingEncoding:(NSStringEncoding)encoding {
    CFStringEncoding cfEncoding = CFStringConvertNSStringEncodingToEncoding(encoding);
    NSString *rfcEscaped = (NSString *)CFURLCreateStringByAddingPercentEscapes(
                                                NULL, 
                                                (CFStringRef)self,
                                                NULL, 
                                                (CFStringRef)@";/?:@&=$+{}<>,",
                                                cfEncoding);
    return [rfcEscaped autorelease];
}
@end

With this Category in place, the original problem could be correctly solved with the following:

NSString *urlEscapedBase = [@"http://server.com/file.php" stringByAddingPercentEscapesUsingEncoding:
                                            NSUTF8StringEncoding];
NSString *rfcEscapedName = [nameField.text stringByAddingRFC3875PercentEscapesUsingEncoding:
                                                NSUTF8StringEncoding];
NSString *rfcEscapedTags = [tagsField.text stringByAddingRFC3875PercentEscapesUsingEncoding:
                                                NSUTF8StringEncoding];
NSString *rfcEscapedEntry = [dreamEntry.text stringByAddingRFC3875PercentEscapesUsingEncoding:
                                                NSUTF8StringEncoding];

NSString *urlStr = [NSString stringWithFormat:@"%@?name=%@&tags=%@&entry=%@",
                                urlEscapedBase,
                                rfcEscapedName,
                                rfcEscapedTags,
                                rfcEscapedEntry];

NSURL *url = [NSURL URLWithString:urlStr];

This is a little variable heavy just be more clear. Also note that the variable list provided to stringWithFormat: should not be nil terminated. The format string describes the precise number of variables that should follow it. Also, technically the strings for query string names (name, tags, entry,..) should be run through stringByAddingPercentEscapesUsingEncoding: as a matter of course but in this small example we can easily see that they contain no invalid URL characters.

To see why the previous solutions are incorrect, imagine that the user input text in dreamEntry.text contains an &, which is not unlikely. With the previous solutions, all text following that character would be lost by the time the server got that text, since the unescaped ampersand would be interpreted by the server as ending the value portion of that query string pair.

like image 184
barrycburton Avatar answered Oct 30 '22 09:10

barrycburton


You're not supposed to URL-escape the entire string, you're supposed to URL-escape the dynamic components. Try

NSString *urlStr = [NSString stringWithFormat:@"http://server.com/file.php?name=%@&tags=%@&entry=%@",
                        [nameField.text stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding],
                        [tagsField.text stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding],
                        [dreamEntry.text stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding],
                        nil];
NSURL *url = [NSURL URLWithString:urlStr];

The second issue with your code (and undoubtedly the reason for the odd printing) is you're passing the string directly to NSLog, so it's being treated as a format string. You need to use

NSLog(@"%@", encodedString);

instead. That will make it print as expected.

Edit: A third issue with your code is you're mixing autoreleased and owned objects, then releasing them all at the end. Go look at the 3 objects you create, and which you subsequently release later. One of them shouldn't be released later because it was produced by a method that did not start with the words alloc, copy, or new. Identifying the object in question is an exercise left to the reader.

like image 26
Lily Ballard Avatar answered Oct 30 '22 09:10

Lily Ballard