まとめ
URLのQueryのvalue用のCharacterSetを作成して、addingPercentEncoding
を使ってパーセントエンコーディングを行います。
はじめに
例えば、パーセントエンコーディングしたいクエリを含んだURLを生成したいと思います。
今回はURLComponentsでURLを組み立ててみることにします。
クエリはパーセントエンコーディングしたいので、addingPercentEncoding
でurlQueryAllowed
を設定してURLを生成してみました。
var c = URLComponents()
c.scheme = "https"
c.host = "example.com"
c.path = "/book"
c.query = "title=独習C++".addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed)
let myUrl: URL = c.url!
print(myUrl) // https://example.com/book?title=%25E7%258B%25AC%25E7%25BF%2592C++
一見日本語の部分がパーセントエンコーディングされているのでうまくいっているように見えますが、titleに含まれている+
がエンコーディングされていません。
Queryのパーセントエンコーディングに必要な文字の参考として
WikipediaのQuery stringを参考にしています。
- SPACE is encoded as '+' or '%20'
- Letters (A–Z and a–z), numbers (0–9) and the characters '~','-','.' and '_' are left as-is
- '+' is encoded by %2B
ですが、CharacterSet.urlQueryAllowedには+
が含まれているため、addingPercentEncodingでは+のエンコーディングが除外されます。
CharacterSetに含まれている文字は以下のようなコードで確かめられます。
参考: https://stackoverflow.com/questions/43322441/is-there-any-reasonable-way-to-access-the-contents-of-a-characterset/51200984#51200984
var offset = 0
for ( var i, w ) in CharacterSet.urlQueryAllowed.bitmapRepresentation.enumerated() {
if i % 8193 == 8192 {
offset += 1
continue
}
i -= offset
if w != 0 {
for j in 0 ..< 8 {
if w & ( 1 << j ) != 0 {
if let unicodeScalar = Unicode.Scalar(i * 8 + j) {
print(unicodeScalar)
}
}
}
}
}
urlQueryAllowedは個別のクエリのvalueためのものではなく、クエリ全体のテキストに対するAPIの想定かと思われます。
For example, in the URL http://www.example.com/index.php?key1=value1#jumpLink, the query component is key1=value1.
ですので、クエリのvalue用のCharacterSetを作成することでクエリのvalueに対応します。
クエリのvalue用のCharacterSetを作成
前出のQuery stringのURL encodingの条件、英数字と一部記号を含めたCharacterSetをurlQueryValueAllowed
として用意します。
extension CharacterSet {
static var urlQueryValueAllowed: CharacterSet {
.alphanumerics.union(.init(charactersIn: "~-._"))
}
}
addingPercentEncodingを行ってみます。
var c = URLComponents()
c.scheme = "https"
c.host = "example.com"
c.path = "/book"
c.queryItems = [.init(name: "title", value: "独習C++".addingPercentEncoding(withAllowedCharacters: .urlQueryValueAllowed))]
let myUrl: URL = c.url!
print(myUrl) // https://example.com/book?title=%25E7%258B%25AC%25E7%25BF%2592C%252B%252B
無事、日本語や+も含めたパーセントエンコーディングが成功しました。