I was curious about what String.addingPercentEncoding (withAllowedCharacters :) encodes, so I looked it up. I also compared the value part of queryItems in URLComponents with encodeURI and encodeURIComponent in JavaScriptCore. It's annoying. By the way, if you try to encode all the symbols and use .alphanumerics, there is a trap that accented alphabets (such as é) and so-called double-byte alphanumeric characters are not encoded.
CharacterSet | ! | " | # | $ | % | & | ' | ( | ) | * | + | , | - | . | / | : | ; | < | = | > | ? | @ | [ | |] | ^ | _ | ` | { | | | } | ~ | |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
urlFragmentAllowed | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ||||||||||||||||||
urlHostAllowed | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | |||||||||||||||||
urlPasswordAllowed | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ||||||||||||||
urlPathAllowed | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ||||||||||||||||
urlQueryAllowed | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ||||||||||||||||||
urlUserAllowed | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ||||||||||||||
URLComponents | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ||||||||||||||||
encodeURI | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | |||||||||||||||||||
encodeURIComponent | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
import Foundation
import JavaScriptCore
let asciiSymbols = (0x20...0x7e)
.map { Unicode.Scalar($0) }
.filter { !CharacterSet.alphanumerics.contains($0) }
let allowdChars = { (str: String) -> String in
let regexp = try! NSRegularExpression(pattern: "%[0-9A-F][0-9A-F]")
let range = NSRange(str.startIndex..., in: str)
return regexp.stringByReplacingMatches(in: str, range: range, withTemplate: "")
}
let urlComponents = { () -> String in
var comp = URLComponents(string: "https://example.com/")!
comp.queryItems = [URLQueryItem(name: "q", value: asciiSymbols.map { String($0) }.joined())]
return allowdChars(comp.url!.absoluteString.components(separatedBy: "=")[1])
}()
let context = JSContext()!
context.evaluateScript("var ascii = ''; for (var i = 0x20; i < 0x7f; i++) ascii += String.fromCharCode(i)")
context.evaluateScript("var result = encodeURI(ascii)")
let encodeURI = allowdChars(context.objectForKeyedSubscript("result")!.toString()!)
context.evaluateScript("var result = encodeURIComponent(ascii)")
let encodeURIComponent = allowdChars(context.objectForKeyedSubscript("result")!.toString()!)
let charsets: [(String, CharacterSet)] = [
("urlFragmentAllowed", .urlFragmentAllowed),
("urlHostAllowed", .urlHostAllowed),
("urlPasswordAllowed", .urlPasswordAllowed),
("urlPathAllowed", .urlPathAllowed),
("urlQueryAllowed", .urlQueryAllowed),
("urlUserAllowed", .urlUserAllowed),
("URLComponents", CharacterSet(charactersIn: urlComponents)),
("encodeURI", CharacterSet(charactersIn: encodeURI)),
("encodeURIComponent", CharacterSet(charactersIn: encodeURIComponent)),
]
print("|CharacterSet|" + asciiSymbols.map { String($0) }.map { ($0 == "|" ? "|" : $0) + "|" }.joined())
print("|:--|" + asciiSymbols.map { _ in ":-:|" }.joined())
for cs in charsets {
print("|\(cs.0)|" + asciiSymbols.map { (cs.1.contains($0) ? "" : "✅") + "|" }.joined())
}