7

I have obj-C method that encodes String:

- (NSString *) encodeValue:(NSString*) unescaped{
    return [unescaped stringByAddingPercentEncodingWithAllowedCharacters:
             [NSCharacterSet URLHostAllowedCharacterSet]];
}

input: testswiftapppod://

output: testswiftapppod%3A%2F%2F

I wrote the same method in Swift but got different output: testswiftapppod:%2F%2F

static func encodeValue(unescaped:String!) -> String{
   return unescaped.addingPercentEncoding(
        withAllowedCharacters: CharacterSet.urlHostAllowed)!
}

For some reason colon not converted

How to fix this issue?

I use Xcode 8.3

[EDIT]

From Docs:

// Returns a new string made from the receiver by replacing all characters not in the allowedCharacters set with percent encoded characters. UTF-8 encoding is used to determine the correct percent encoded characters. Entire URL strings cannot be percent-encoded. This method is intended to percent-encode an URL component or subcomponent string, NOT the entire URL string. Any characters in allowedCharacters outside of the 7-bit ASCII range are ignored. - (nullable NSString *)stringByAddingPercentEncodingWithAllowedCharacters:(NSCharacterSet *)allowedCharacters NS_AVAILABLE(10_9, 7_0);

snaggs
  • 5,543
  • 17
  • 64
  • 127
  • It is strange that the Objective-C code behaves differently, but a colon *is* allowed in the host part of an URL, e.g. `http://myhost.com:myport/path` – Martin R Apr 06 '17 at 12:04
  • As a colon is allowed in URL encoding. Just a question, have you tried something like "unescaped.stringByAddingPercentEncodingForFormData()" yet? – Lepidopteron Apr 06 '17 at 12:05
  • Also "testswiftapppod://" looks more like an URL scheme, why do you need to escape it at all? – Martin R Apr 06 '17 at 12:08
  • @MartinR because I want to put it as argument to URL – snaggs Apr 06 '17 at 12:09

2 Answers2

8

EDIT:

This is probably undocumented but intended behavior. See is `addingPercentEncoding` broken in Xcode 9 beta 2? for more details.


This is a bug.

I went over different cases, it seems all Swift code works correctly. Note that : is allowed in URL host, therefore it should not be encoded and the bug is in the Obj-C version.

NSCharacterSet *set = [NSCharacterSet URLHostAllowedCharacterSet];    
NSLog(@"Colon is member: %@", [set characterIsMember:':'] ? @"true" : @"false"); // prints true

It's an interesting bug because if you add ":" to the character set manually

NSMutableCharacterSet *set = [[NSCharacterSet URLHostAllowedCharacterSet] mutableCopy];
[set addCharactersInString:@":"];

Everything starts to work correctly.

Report it.

Note that when encoding for URL parameters, you shouldn't use urlHostAllowed. If possible, use NSURLQuery to build your URL instead. Neither of the predefined sets is actually suitable for URL encoding. You can start with urlQueryAllowed but you still have to remove some characters from it.

See for example this answer for a correct solution or for example the implementation in Alamofire library.

Sulthan
  • 128,090
  • 22
  • 218
  • 270
1

The desired output can be generated by:

func encodeValue(_ string: String) -> String? {
    guard let unescapedString = string.addingPercentEncoding(withAllowedCharacters: CharacterSet(charactersIn: ":/").inverted) else { return nil }

    return unescapedString
}

let encodedString = encodeValue("testswiftapppod://") // testswiftapppod%3A%2F%2F
Ahmad F
  • 30,560
  • 17
  • 97
  • 143