はじめに
CX事業本部の中安です。まいどです。
ネットワーク通信ライブラリの代表格であるAlamofire
を使用したアプリを開発しているのですが、
とあるGETで取得するAPIに通信しようとする際に、URLクエリパラメータの仕様で少し悩ましい部分がありました。
その仕様とは「ひとつのキーに複数の値を渡すときに、その個数分だけそのキーを使う」というものです。
文章だとわかりづらいので、例を出してみます。
https://(APIのエンドポイント)?loc=tokyo&loc=osaka&loc=nagoya&date=20230509
この例では、loc
というキーに対して tokyo
osaka
nagoya
という複数の値を渡そうとしています。
APIの仕様としてスタンダードなのかどうかは分かりませんが、こういう仕様なのでそれに合わせないといけません。
このうえで、Alamofire
に対してはEncodable
なリクエストパラメータオブジェクトを渡すことになっています。
それは、次項で例示したいと思います。
元々のソースコード
こちらが実際に開発していたアプリに近しい作りのソースコードです。
リクエストパラメータ用の構造体を定義し、それをインスタンス化して、Alamofire
のセッション(AF)に渡してリクエストしています。
// リクエストパラメータの定義
struct ExampleAPIRequestParameters: Encodable {
let date: String
let location: String
enum CodingKeys: String, CodingKey {
case date
case location = "loc"
}
}
// リクエストパラメータオブジェクトの作成
let params = ExampleAPIRequestParameters(
date: "20230509",
location: "tokyo"
)
// Alamofireを使用したリクエスト実行
let url = "http://(APIのエンドポイント)"
AF.request(url, method: .get, parameters: params).responseData { response in
// 何か処理
}
しかし、上記のソースコードではloc
は複数の値を渡せません。
文字列配列に変える
loc
に複数の値を渡すには、もちろん文字列型を文字列配列型に変えるわけです。
struct ExampleAPIRequestParameters: Encodable {
let date: String
let locations: [String]
enum CodingKeys: String, CodingKey {
case date
case locations = "loc"
}
}
let params = ExampleAPIRequestParameters(
date: "20230509",
locations: ["tokyo", "osaka", "nagoya"]
)
これで解決と思いきや、残念ながらAlamofire
で変換されるURLは以下のようになります。
https://(APIのエンドポイント)?loc[]=tokyo&loc[]=osaka&loc[]=nagoya&date=20230509
URLクエリパラメータで配列であることを示すブラケット([]
)がついてしまうのです。
URLクエリパラメータの仕様としては間違っていませんが、APIの仕様には合わないものとなってしまいました。
ブラケットを取り払う
このブラケットが付いてしまう原因は、Alamofire
が用意するパラメータエンコーダが関与しています。
AF.request()
をする際に、パラメータエンコーダは指定しない限りはURLEncodedFormParameterEncoder.default
という定義のエンコーダを使用し、その中身は「配列にはブラケットを付けよ」という設定がなされているのでした。
なので、ブラケットを取り払うにはデフォルトのものは使用せず、自分でエンコーダを作ってあげる必要があります。
// 「配列にはブラケットを付けない」という設定のエンコーダを作成する
let arrayToNoBracketsURLEncoder = URLEncodedFormEncoder(arrayEncoding: .noBrackets)
// 上記のものをエンコーダに設定したパラメータエンコーダを作成する
let parameterEncoder = URLEncodedFormParameterEncoder(encoder: arrayToNoBracketsURLEncoder)
// リクエスト時に上記のパラメータエンコーダを渡して実行する
let url = "http://(APIのエンドポイント)"
AF.request(url, method: .get, parameters: params, encoder: parameterEncoder).responseData { response in
// 何か処理
}
こうすることにより、求めていたパラメータの形式でリクエストがされるようになりました。
https://(APIのエンドポイント)?loc=tokyo&loc=osaka&loc=nagoya&date=20230509
このように、ちょっと変わった仕様のAPIに対してもクエリパラメータのフォーマットを変更することができるようになります。
指定できる配列のフォーマット
ここでは ArrayEncoding
を.noBrackets
を指定しましたが、他にはどんな設定ができるかというと
- .brackets: ブラケットをつける
loc[]=tokyo&loc[]=osaka&loc[]=nagoya
- .noBrackets: ブラケットをつけない
loc=tokyo&loc=osaka&loc=nagoya
- .indexInBrackets: ブラケットに添字をつける
loc[0]=tokyo&loc[1]=osaka&loc[2]=nagoya
その他指定できる設定
ここまで紹介した配列のフォーマットの他にも、URLEncodedFormEncoder
を作る際には以下のような設定を施すことが可能です。
- alphabetizeKeyValuePairs: キーをアルファベット順に並べるかどうか
- arrayEncoding: 配列のフォーマット (ここまで述べた通り)
- boolEncoding: Bool型を
true/false
1/0
で記述するかを設定できる - dataEncoding: バイナリデータをBase64や他の形式でクエリ化できる
- dateEncoding: 日付型をどのような形式で文字列化するかを設定できる
- keyEncoding: キー名をキャメルケースやスネークケースに変えることができる
- spaceEncoding: 空白スペースを「+」にしたり、エスケープ文字にすることができる
- allowedCharacters: 使用できる文字を制御できる
終わりに
Alamofire
でURLクエリパラメータを指定する際には、それなりに柔軟なフォーマットや設定に対応してくれていることがわかりました。
もし、同じようなところで躓いた方の何かの参考になれば幸いです。
では、またー