Edited at

iOSでパーセントエンコード(Swift)

More than 3 years have passed since last update.

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