URLのクエリパラメータや、x-form-urlencodedのbodyで使うためのエスケープ関数を作りたい。
こういうやつ。
"q=" + uriEncode("a") // => "q=a"
"q=" + uriEncode("a b") // => "q=a%20b"
"q=" + uriEncode("あ") // => "q=%E3%81%82"
stringByAddingPercentEscapesUsingEncoding
最初に見つけたやつがこれ。
func uriEncode(str: String) -> String {
return str.stringByAddingPercentEscapesUsingEncoding(NSUTF8StringEncoding)!
}
上の仕様だけなら実はこれで成り立つ。しかし、いくつか問題になりそうなやつがある。
"q=" + uriEncode("=") // => "q=="
"q=" + uriEncode("&") // => "q=&"
ツライ。
CFURLCreateStringByAddingPercentEscapes
これじゃ駄目だよということでよく見かけるやつが、 CFURLCreateStringByAddingPercentEscapes
を使う方法。
しかし、iOS7からは別の便利なAPIが提供されたそうなので、Swiftだったらそっちを使ったほうが良さそう。
stringByAddingPercentEncodingWithAllowedCharacters
iOS7からのAPIがこれ。
func uriEncode(str: String) -> String {
return str.stringByAddingPercentEncodingWithAllowedCharacters(NSCharacterSet.alphanumericCharacterSet())!
}
よさそう。
"q=" + uriEncode("=") // => "q=%3D"
"q=" + uriEncode("&") // => "q=%26"
しかし、alphanumericCharacterSet
だと上のブログ記事にもあるように、RFC3986的にはエスケープしなくても良いハイフンとかアンダースコアまでエスケープしてしまう。
"q=" + uriEncode("-") // => "q=%2D"
"q=" + uriEncode("_") // => "q=%5F"
これはこれでウェブサーバーは正しく扱ってくれるような気がするけどね。
URLQueryAllowedCharacterSet
NSCharacterSet.URLQueryAllowedCharacterSet
というのがあって、一見使えそうに見えるがこれは全然駄目。
// これは駄目なコード
func uriEncode(str: String) -> String {
return str.stringByAddingPercentEncodingWithAllowedCharacters(NSCharacterSet.URLQueryAllowedCharacterSet())!
}
uriEncode("!*'();:@&=+$,/?%#[]-._~ ") // => "!*'();:@&=+$,/?%25%23%5B%5D-._~%20"
特にイコールやアンパサンドがエスケープされていない。
RFC3986準拠
ハイフン、アンダースコア、チルダ、ピリオドをきちんと除外してRFC3986で規定されているような形にするには、たぶん今のところはこうやるしかないのかなと思う。
func uriEncode(str: String) -> String {
var allowedCharacterSet = NSMutableCharacterSet.alphanumericCharacterSet()
allowedCharacterSet.addCharactersInString("-._~")
return str.stringByAddingPercentEncodingWithAllowedCharacters(allowedCharacterSet)!
}
uriEncode("!*'();:@&=+$,/?%#[]-._~ ") // => "%21%2A%27%28%29%3B%3A%40%26%3D%2B%24%2C%2F%3F%25%23%5B%5D-._~%20"
まとめ
Swift (iOS7以上)で使うならこれでたぶん十分。
func uriEncode(str: String) -> String {
return str.stringByAddingPercentEncodingWithAllowedCharacters(NSCharacterSet.alphanumericCharacterSet())!
}
RFC3986に従いたい人はこうする。
func uriEncode(str: String) -> String {
var allowedCharacterSet = NSMutableCharacterSet.alphanumericCharacterSet()
allowedCharacterSet.addCharactersInString("-._~")
return str.stringByAddingPercentEncodingWithAllowedCharacters(allowedCharacterSet)!
}