WWDC関連のイベントが落ち着いたと思ったらすっかり夏バテ気味のiOSチームのかっくん(@fromkk)です。
気温の変化も大きいので夏風邪などひかない様に気をつけましょうね。
最近はREST API等も増えて来て、サーバーに直接JSONをPOSTしたり、進んでるところはProtocol Buffer等を利用してサーバーとやりとりしているかと思いますが、
未だに旧来のGETやPOSTでクエリー文字列を利用する事もあるかと思います。
iOSでは何故かこの辺りの環境が整備されておらず、力技で解決してきた人も多いのでは無いでしょうか?
これまで
例えば日本郵便を例にすると、
let pref: Int = 13 let url = URL(string: "http://www.post.japanpost.jp/cgi-zip/zipcode.php?pref=\(pref)")! // get data from url
の様な形でURLを作っていた事も多かったかと思います。
パラメーターが1つ2つ程度ならこれでも十分対応可能ですが、パラメーターが増えたり、ネストが深くなったりすると大変ですよね。
こういう煩雑さを解消する為に作成したライブラリがこちらです。
fromkk/URLQueryBuilder
インストール
今の所 Carthage
にのみ対応しています。
Cartfile
に github "fromkk/URLQueryBuilder"
を追記して carthage update
し、Carthage/Build
内の URLQueryBuilder.framework
へのリンクを追加します。
使い方
URLQueryBuilder
の生成時に Dictionary
を渡して build
メソッドを呼ぶだけです。
import URLQueryBuilder let queryString = URLQueryBuilder(dictionary:["pref": 13]).build() let url = URL(string: "http://www.post.japanpost.jp/cgi-zip/zipcode.php?\(queryString)")! // get data from url
URLエンコードしたい事もあるかと思ったので build(with: [.urlEncode])
とすればクエリーをエスケーピングする事が出来ます。
// multibytes let someDictionary: [String: Any] = ["key": "マルチバイト😇"] let queryString: String = URLQueryBuilder(dictionary: someDictionary).build(with: [.urlEncoding]) //key=%E3%83%9E%E3%83%AB%E3%83%81%E3%83%90%E3%82%A4%E3%83%88%F0%9F%98%87
複数階層や配列にも対応しています。
// deep hierarchy let deepDictionary: [String: Any] = ["key": ["subkey": "subvalue"]] let queryString: String = URLQueryBuilder(dictionary: deepDictionary).build() //key[subkey]=subvalue
// array let arrayDictionary: [String: Any] = ["array": ["hello", "world"]] let queryString: String = URLQueryBuilder(dictionary: arrayDictionary).build() //array[0]=hello&array[1]=world
他ライブラリとの連携
元メルカリ石川さん作の ishkawa/APIKit の FormURLEncodedBodyParameters
では深い階層には対応していない様です。
import APIKit import URLQueryBuilder struct DeepFormURLEncodedBodyParameters: BodyParameters { let dictionary: [String: Any] var contentType: String { return "application/x-www-form-urlencoded" } func buildEntity() throws -> RequestBodyEntity { let post = URLQueryBuilder(dictionary: self.dictionary).build(with: [.urlEncoding]) return RequestBodyEntity.data(post.data(using: .utf8)!) } } struct YourRequest: Request { typealias Response = YourResponse var baseURL: URL = ... var path: String = ... var method: HTTPMethod = ... var bodyParameters: BodyParameters? { return DeepFormURLEncodedBodyParameters(dictionary: ["hello": ["deep": "hierarchy"]]) } }
上記の様にする事で複数階層の FormURLEncodedBodyParameters
を作成する事が出来ました。
今後
Bool
型の扱いに困ったので今回は非対応にしていますが、その内対応するかもしれません。
(false
, true
なのか 0
なのか 1
なのか環境によって変わりそうなので。。)
まとめ
これまでもサーバーにアクセスするコードをいくつか書いてきましたが、パラメーターが複数階層に及ぶ事はなかなか無かったのでこれまであまり困る事は無かったんですが、いざ作ってみると凄く便利で今後よく利用する事になりそうな気がします。
もし気に入って頂けた場合リポジトリにスター🌟を頂けたら嬉しいです!
不具合等あった場合もissueやPR待ってます!
宣伝
そういえばiOSDC 2017でLTを話す予定なので聞きに来て頂けたら嬉しいです!
自分が欲しいとアプリを作った 5分(LT)
(※盛大にタイトル間違えてますがこのまま突っ走ります)
積極採用中!!
子育て家族アプリFamm、カップル専用アプリPairyを運営するTimers inc. では、現在エンジニアを積極採用中! 急成長中のサービスの技術の話を少しでも聞いてみたい方、スタートアップで働きたい方など、是非お気軽にご連絡ください! 採用HP : http://timers-inc.com/engineerings