stringByAddingPercentEncodingWithAllowedCharacters

Can someone please tell me which character encoding I should be using with the stringByAddingPercentEncodingWithAllowedCharacters in order to encode a URL parameter?


There's a lot of conflicting information about this floating around on the internet.


I had been using URLQueryAllowedCharacterSet, but found that it was not escaping the "+" character, which caused the receiving end to decode a space whenever the app was trying to send a + character (this happens, for example, in some email addresses).


For now, I switched to using alphanumericCharacterSet, which seems like overkill.


Frank

Hello flarosa,

Why not just create your own character set using URLQueryAllowedCharacterSet as a base, and remove the '+' character?

One reason I don't do this is because I want to do it the right way, not some way that seems right to me.


Another reason is because I write a lot of apps and do this in a lot of different places, so I don't want to have to remember a complicated sequence of code every time.

The framework ought to provide a straightforward way of doing this.

Accepted Answer

The framework ought to provide a straightforward way of doing this.

I do agree, but as far as I know, Apple does not provide one we need.


As you see, `URLQueryAllowedCharacterSet` contains `+`, `&` and `=`, which means they are not escaped so servers may not properly decode the query params when such characters included.


Currently I use my own CharacterSet when constructing URL with query params.

extension CharacterSet {
    static let rfc3986Unreserved = CharacterSet(charactersIn:
        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-._~")
}


Some servers (not many) do not accept `%20` for space character and only accept `+`, in such cases, `stringByAddingPercentEncodingWithAllowedCharacters` does not work and I need to use something like this:

extension String {
    func addingPercentEncoding(withAllowedCharacters characterSet: CharacterSet, using encoding: String.Encoding) -> String {
        let stringData = self.data(using: encoding, allowLossyConversion: true) ?? Data()
        let percentEscaped = stringData.map {byte->String in
            if characterSet.contains(UnicodeScalar(byte)) {
                return String(UnicodeScalar(byte))
            } else if byte == UInt8(ascii: " ") {
                return "+"
            } else {
                return String(format: "%%%02X", byte)
            }
            }.joined()
        return percentEscaped
    }
}


(I'm not sure you prefer Swift or not, but you understand that we need to do it by ourselves.)

Anyway, what is right depends on servers and no predefined `NSCharacterSet` are right for most servers.

Thanks, I appreciate the explanation.


I've decided to just use the default 'alphanumericCharacterSet' for now, figuring, better to over-do it and encode stuff that doesn't need encoding than risk having a problem.

stringByAddingPercentEncodingWithAllowedCharacters
 
 
Q