これは、クエリパラメータをベースURLに追加する方法です。
_let baseURL: URL = ...
let queryParams: [AnyHashable: Any] = ...
var components = URLComponents(url: baseURL, resolvingAgainstBaseURL: false)
components?.queryItems = queryParams.map { URLQueryItem(name: $0, value: "\($1)") }
let finalURL = components?.url
_
この問題は、値の1つに_+
_記号が含まれている場合に発生します。何らかの理由で、最終的なURLでは_%2B
_にエンコードされず、代わりに_+
_のままです。自分でエンコードして_%2B
_を渡すと、NSURL
は_%
_をエンコードし、「プラス」は_%252B
_になります。
問題は、NSURL
のインスタンスに_%2B
_を含めるにはどうすればよいですか?
追伸自分でクエリ文字列を作成し、その結果をNSURL
のコンストラクターinit?(string:)
に渡すだけでも、この問題は発生しません。
他の回答で指摘されているように、「+」文字はクエリ文字列で有効ですが、これは queryItems
ドキュメント。
一方、 RIアドレス指定に関するW3Cの推奨事項 は、
クエリ文字列内では、プラス記号はスペースの省略表記として予約されています。したがって、実際のプラス記号はエンコードする必要があります。このメソッドは、スペースを許可しないシステムでクエリURIを渡しやすくするために使用されました。
これは、カスタム文字セットを使用して、パーセントでエンコードされたクエリ文字列を「手動で」構築することで実現できます。
let queryParams = ["foo":"a+b", "bar": "a-b", "baz": "a b"]
var components = URLComponents()
var cs = CharacterSet.urlQueryAllowed
cs.remove("+")
components.scheme = "http"
components.Host = "www.example.com"
components.path = "/somepath"
components.percentEncodedQuery = queryParams.map {
$0.addingPercentEncoding(withAllowedCharacters: cs)!
+ "=" + $1.addingPercentEncoding(withAllowedCharacters: cs)!
}.joined(separator: "&")
let finalURL = components.url
// http://www.example.com/somepath?bar=a-b&baz=a%20b&foo=a%2Bb
別のオプションは、生成されたパーセントエンコードされたクエリ文字列のプラス文字を「ポストエンコード」することです。
let queryParams = ["foo":"a+b", "bar": "a-b", "baz": "a b"]
var components = URLComponents()
components.scheme = "http"
components.Host = "www.example.com"
components.path = "/somepath"
components.queryItems = queryParams.map { URLQueryItem(name: $0, value: $1) }
components.percentEncodedQuery = components.percentEncodedQuery?
.replacingOccurrences(of: "+", with: "%2B")
let finalURL = components.url
print(finalURL!)
// http://www.example.com/somepath?bar=a-b&baz=a%20b&foo=a%2Bb
URLComponentsは正常に動作しています:_+
_は、現状では合法であるため、パーセントエンコードされていません。すでにForest Kuneckeによって説明されているように、_+
_を使用して_.alphanumerics
_をパーセント符号化強制することができます(同じ結果が独立して得られましたが、彼は私の前に彼の答えを提出してください!).
ほんのいくつかの改良。これが文字列の場合、OPのvalue: "\($1)"
は不要です。 _value:$1
_と言うだけです。そして、そのすべてのコンポーネントからURLを形成する方が良いでしょう。
したがって、これは基本的にForest Kuneckeと同じソリューションですが、より標準的であり、最終的にはよりコンパクトになると思います。
_let queryParams = ["hey":"ho+ha"]
var components = URLComponents()
components.scheme = "http"
components.Host = "www.example.com"
components.path = "/somepath"
components.queryItems = queryParams.map {
URLQueryItem(name: $0,
value: $1.addingPercentEncoding(withAllowedCharacters: .alphanumerics)!)
}
let finalURL = components.url
_
[〜#〜] edit [〜#〜]マーティンRからの提案された修正後、おそらくより良い:クエリ全体を形成し、ピースを自分でパーセントエンコードして、URLComponentsにそうしました:
_let queryParams = ["hey":"ho+ha", "yo":"de,ho"]
var components = URLComponents()
components.scheme = "http"
components.Host = "www.example.com"
components.path = "/somepath"
var cs = CharacterSet.urlQueryAllowed
cs.remove("+")
components.percentEncodedQuery = queryParams.map {
$0.addingPercentEncoding(withAllowedCharacters: cs)! +
"=" +
$1.addingPercentEncoding(withAllowedCharacters: cs)!
}.joined(separator:"&")
// ---- Okay, let's see what we've got ----
components.queryItems
// [{name "hey", {some "ho+ha"}}, {name "yo", {some "de,ho"}}]
components.url
// http://www.example.com/somepath?hey=ho%2Bha&yo=de,ho
_
addingPercentEncoding(withAllowedCharacters: .alphanumerics)
を使ってみませんか?
これがどのように機能するかを示す簡単な遊び場を作成しました。
//: Playground - noun: a place where people can play
let baseURL: URL = URL(string: "http://example.com")!
let queryParams: [AnyHashable: Any] = ["test": 20, "test2": "+thirty"]
var components = URLComponents(url: baseURL, resolvingAgainstBaseURL: false)
var escapedComponents = [String: String]()
for item in queryParams {
let key = item.key as! String
let paramString = "\(item.value)"
// percent-encode any non-alphanumeric character. This is NOT something you typically need to do. User discretion advised.
let escaped = paramString.addingPercentEncoding(withAllowedCharacters: .alphanumerics)
print("escaped: \(escaped)")
// add the newly escaped components to our dictionary
escapedComponents[key] = escaped
}
components?.queryItems = escapedComponents.map { URLQueryItem(name: ($0), value: "\($1)") }
let finalURL = components?.url