AlamofireとCodableを使った実装を試してみました。
Alamofireは事前にCocoaPodsでインストールしておきます。
Alamofire+Codableの事前準備
target 'MyApp' do use_frameworks! pod 'Alamofire' end
もし非SSL(http)通信をする場合はApp Transport Security Settingsの設定を事前にしておいてください。
Alamofire+Codableの簡単な使い方
まずは下のような簡単な構造のデータをAPI経由で受け取ってみます。
{ id: 1, name: 'my_name' }
最初にCodableに準拠した構造体を作ります。
struct User: Codable { let id: Int let name: String }
構造体を作ったらサーバーからのレスポンスを構造体に変換する処理を実装します。
実装は下の通りです。
この例ではgetでuserのデータを取得しています。
Alamofire.request("http://localhost:3000/users/1.json").response { response in if let data = response.data { let result = try? JSONDecoder().decode(User.self, from: data) print(result) } }
ログを見ると無事にデータが取得できている事が分かります。
Codableの型が違う場合
下のようにresponseと構造体の型が違う場合の挙動も見てみます。
struct User: Codable { let id: Int let name1: String // name → name1に変更 }
下のようにtry?
を使っている場合はnilが返ってきます。
let result = try? JSONDecoder().decode(User.self, from: data) // → nil
try!
を使っている場合はアプリが落ちます。
let result = try! JSONDecoder().decode(User.self, from: data) // → クラッシュ
エラーメッセージは下の通りです。
Fatal error: 'try!' expression unexpectedly raised an error: Swift.DecodingError.keyNotFound(CodingKeys(stringValue: "name1", intValue: nil), Swift.DecodingError.Context(codingPath: [], debugDescription: "No value associated with key CodingKeys(stringValue: \"name1\", intValue: nil) (\"name1\").", underlyingError: nil)): file /BuildRoot/Library/Caches/com.apple.xbs/Sources/swiftlang/swiftlang-902.0.54/src/swift/stdlib/public/core/ErrorType.swift, line 184 2018-06-23 22:45:49.750172+0900 MyApp[19279:6248046] Fatal error: 'try!' expression unexpectedly raised an error: Swift.DecodingError.keyNotFound(CodingKeys(stringValue: "name1", intValue: nil), Swift.DecodingError.Context(codingPath: [], debugDescription: "No value associated with key CodingKeys(stringValue: \"name1\", intValue: nil) (\"name1\").", underlyingError: nil)): file /BuildRoot/Library/Caches/com.apple.xbs/Sources/swiftlang/swiftlang-902.0.54/src/swift/stdlib/public/core/ErrorType.swift, line 184
少し話は逸れますがレスポンスのキーが多い分にはエラーは起きません。
# レスポンス { id: 1, name: 'my_name', name2: 'my_name', name3: 'my_name', name4: 'my_name', }
let result = try? JSONDecoder().decode(User.self, from: data) // → 問題なくデコードできる
Codableで数字をDouble型として扱う
数値型はIntではなくDoubleで受け取る事もできます。
下のように変えるだけで問題なくデコードします。
struct User: Codable { let id: Double let name: String }
ただし数値を文字列にする事はできません。
struct User: Codable { let id: String let name: String }
Codableで複数オブジェクトをパースする
下のように複数データが返ってくる場合について見ていきます。
[{ id: 1, name: 'my_name', },{ id: 2, name: 'my_name2', },{ id: 3, name: 'my_name3', }]
複数のデータが返る時はUser.selfをArray
Alamofire.request("http://localhost:3000/users.json").response { response in if let data = response.data { let result = try? JSONDecoder().decode(Array<User>.self, from: data) // User.selfをArray<User>.selfに置き換えただけ print(result) } }
ログを見るとしっかりデコードされている事が分かります。
Codableでスネークケースのデータを受け取る
次のようにサーバーからのレスポンスはスネークケース、iOSアプリはキャメルケースの場合も見ていきます。
# レスポンスはスネークケース { id: 1, name: 'my_name', book_count: 1 }
// 構造体はキャメルケース struct User: Codable { let id: Int let name: String let bookCount: Int }
こういった場合はJSONDecoderのkeyDecodingStrategyオプションを利用します。
keyDecodingStrategyに.convertFromSnakeCaseをセットする事でスネークケースとキャメルケースの変換をしてくれるようになります。
Alamofire.request("http://localhost:3000/users/1.json").response { response in if let data = response.data { let decoder = JSONDecoder() decoder.keyDecodingStrategy = .convertFromSnakeCase // スネークケースとキャメルケースの変換をするオプション let result = try? decoder.decode(User.self, from: data) print(result) } }
ログを見ると無事に変換できている事が分かります。
keyDecodingStrategyですがSwift4.1から登場したのでそれ以前のSwiftでは使えません。
CodableでDate型を扱う
下のようにサーバーがDate型のプロパティーを返す時も確認します。
{ id: 1, name: 'my_name', created_at: '2018-01-01T00:00:00.000Z', updated_at: '2018-01-01T00:00:00.000Z' }
まずはレスポンスに合った構造体を作ります。
struct User: Codable { let id: Int let name: String let createdAt: Date let updatedAt: Date }
デコード処理は下の通りです。
JSONDecoderのdateDecodingStrategyというプロパティーにDateの形式をセットします。
Alamofire.request("http://localhost:3000/users/1.json").response { response in if let data = response.data { let decoder = JSONDecoder() let formatter = DateFormatter() formatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ss.SSS'Z'" decoder.dateDecodingStrategy = .formatted(formatter) decoder.keyDecodingStrategy = .convertFromSnakeCase let result = try? decoder.decode(User.self, from: data) print(result) } }
ログを見るとDateもしっかりと変換できている事が分かります。
Codableでネスト(入れ子)のデータを扱う
最後に下のようなネスト形式について見ていきます。
{ id: 1, name: 'my_name', books: [ { id: 1, title: 'title1' }, { id: 2, title: 'title2' }, ] }
上のようなデータを受け取りたい場合は下のような構造体を作ります。
struct User: Codable { let id: Int let name: String let books: [Book] } struct Book: Codable { let id: Int let title: String }
デコード部分は今までと同じ処理で問題ありません。
Alamofire.request("http://localhost:3000/users/1.json").response { response in if let data = response.data { let result = try? JSONDecoder().decode(User.self, from: data) print(result) } }
実行してログを見るとUserのbooksプロパティーに値が入っている事が分かります。
まとめ
Swift4から登場したCodableですがかゆいところに手が届く感じでとても便利そうでした。
今まではObjectMapperがメインだったんですが今後はCodableも使ってみようと思います。