NSURLComponents does not correctly handle "+" usage in query parameters.

Originator:hotngui
Number:rdar://24076063 Date Originated:1/6/2016
Status:Closed Resolved:Behaves correct
Product:iOS Product Version:9.x
Classification:Serious Bug Reproducible:AlwaysWhen the "+" (plus sign) is used in a query parameter it needs to be percent encoded, but NSURLComponents does not do that. The result is that when sent to a web server the "+" gets erroneously interpreted as a "space" character when decoded.
 
When the "+" (plus sign) is used in a query parameter it needs to be percent encoded, but NSURLComponents does not do that. The result is that when sent to a web server the "+" gets erroneously interpreted as a "space" character when decoded.

Apple says its working correctly, but I disagree.

Comments

Apple's Response.

Apple Developer Relations21-Jan-2016 01:31 PM

This issue behaves as intended based on the following:

The '+' character is legal in the query component so it does not need to be percent-encoded.

Some systems use the '+' as a space and require '+' the plus character to be percent-encoded. However, that kind of two stage encoding (converting plus sign to %2B and then converting space to plus sign) is prone to errors because it easily leads to encoding problems. It also breaks if the URL is normalized (syntax normalization of URLs includes the removal of all unnecessary percent-encoding -- see rfc3986 section 6.2.2.2).

So, if you need that behavior because of the server your code is talking to, you'll handle that extra transformation(s) yourself. Here's a snippet of code that shows what you need to do both ways:

NSURLComponents components = [[NSURLComponents alloc] init]; NSArray items = [NSArray arrayWithObjects:[NSURLQueryItem queryItemWithName:@"name" value:@"Value +"], nil]; [components setQueryItems:items]; NSLog(@"URL queryItems: %@", [components queryItems]); NSLog(@"URL string before: %@", [components string]); // Replace all "+" in the percentEncodedQuery with "%2B" (a percent-encoded +) and then replace all "%20" (a percent-encoded space) with "+" components.percentEncodedQuery = [[components.percentEncodedQuery stringByReplacingOccurrencesOfString:@"+" withString:@"%2B"] stringByReplacingOccurrencesOfString:@"%20" withString:@"+"]; NSLog(@"URL string after: %@", [components string]); // This is the reverse if you receive a URL with a query in that form and want to parse it with queryItems components.percentEncodedQuery = [[components.percentEncodedQuery stringByReplacingOccurrencesOfString:@"+" withString:@"%20"] stringByReplacingOccurrencesOfString:@"%2B" withString:@"+"]; NSLog(@"URL string back: %@", [components string]); NSLog(@"URL queryItems: %@", [components queryItems]);

The output is:

URL queryItems: ( " {name = name, value = Value +}" ) URL string before: ?name=Value%20+ URL string after: ?name=Value+%2B URL string back: ?name=Value%20+ URL queryItems: ( " {name = name, value = Value +}" )

We are now closing this bug report.

If you have questions about the resolution, or if this is still a critical issue for you, then please update your bug report with that information.


Please note: Reports posted here will not necessarily be seen by Apple. All problems should be submitted at bugreport.apple.com before they are posted here. Please only post information for Radars that you have filed yourself, and please do not include Apple confidential information in your posts. Thank you!