Search code examples
iosswifturlencodexcode9-beta

is `addingPercentEncoding` broken in Xcode 9?


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

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

I get those results:

cases where I get an undesirable behavior

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

    true
    info%3Ahello%20world

cases where I get the expected behavior

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

    true
    info:hello%20world

Is there any workaround to get a working implementation of addingPercentEncoding that will correctly respect the given allowedCharacters?


Solution

  • Apparently there is some undocumented magic done by addingPercentEncoding when the CharacterSet used as reference is an underlying NSCharacterSet class.

    So to workaround this magic, you need to make your CharacterSet a pure Swift object. To do so, I'll create a copy (thanks Martin R!), so that the evil magic is gone:

    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)
        }
    }