Is `addPercentEncoding` broken in Xcode 9?

in Swift 3.x with Xcode 9 beta 2, using addingPercentEncoding gives unexpected results. CharacterSet.urlPathAllowed always contains a ":", so by definition addingPercentEncoding it should never avoid it. However, using this code:

 // always true print(CharacterSet.urlPathAllowed.contains(":")) let myString = "info:hello world" let escapedString = myString.addingPercentEncoding(withAllowedCharacters: .urlPathAllowed)! print(escapedString) 

I get these results:

cases when I get unwanted behavior

  • Xcode 9 beta 2, iOS 9.3
  • Xcode 9 beta 2, iOS 11.0

    right
    Information% 3Ahello% 20world

cases where I get the expected behavior

  • Xcode 9 beta 2, iOS 10.3.1
  • Xcode 8.3.3, any iOS

    right
    info: hello% 20world

Is there any workaround for getting a working implementation of addingPercentEncoding that will respect the given allowedCharacters ?

+5
source share
2 answers

There seems to be some undocumented magic being done by addingPercentEncoding when the CharacterSet used as a reference is the base class of NSCharacterSet.

So, to get around this magic, you need to make your CharacterSet a clean Swift object. To do this, I will create a copy (thanks to Martin R!) So that the evil magic disappears:

 let myString = "info:hello world" let csCopy = CharacterSet(bitmapRepresentation: CharacterSet.urlPathAllowed.bitmapRepresentation) let escapedString = myString.addingPercentEncoding(withAllowedCharacters: csCopy)! //always "info:hello%20world" print(escapedString) 

As an extension:

 extension String { func safeAddingPercentEncoding(withAllowedCharacters allowedCharacters: CharacterSet) -> String? { // using a copy to workaround magic: https://stackoverflow.com/q/44754996/1033581 let allowedCharacters = CharacterSet(bitmapRepresentation: allowedCharacters.bitmapRepresentation) return addingPercentEncoding(withAllowedCharacters: allowedCharacters) } } 
+9
source

The reason that he now has a percentage .urlPathAllowed character : is because .urlPathAllowed now strictly complies with RFC 3986 , which says in Section 3.3 "Paths":

In addition, a reference to a URI (section 4.1) can be a reference to a relative path, in which case the first segment of the path cannot contain a colon (":").

Thus : are allowed in relative ways (what we are dealing with here), but simply not in the first component.

To consider:

 let string = "foo:bar/baz:qux" print(string.addingPercentEncoding(withAllowedCharacters: .urlPathAllowed)!) 

This, in accordance with RFC 3986, encodes in percent : in the first component, but allows it to be unencoded in the following components:

  foo% 3Abar / baz: qux 

This character set is not percent encoding solely on the basis of what characters are in the set, but actually applies the logic of the relative path of RFC 3986. But, as Ker said, if you need to, you can bypass this logic, .urlPathAllowed your own character set with the same allowed characters as .urlPathAllowed , and this new character set will not apply this RFC 3986 logic.

+7
source

Source: https://habr.com/ru/post/1266410/


All Articles