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
}
下のようにtry?
を使っている場合はnilが返ってきます。
let result = try? JSONDecoder().decode(User.self, from: data)
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.selfに置き換えるだけです。
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)
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も使ってみようと思います。