しめ鯖日記

swift, iPhoneアプリ開発, ruby on rails等のTipsや入門記事書いてます

Alamofire+CodableでAPIのレスポンスをオブジェクトに変換

AlamofireとCodableを使ってAPIを叩いてみました。

APIは下のようなデータを返すものにしました。

f:id:llcc:20171006132058p:plain

まずはCodableに準拠した構造体を作ります。

struct User: Codable {
    let key1: String
    let key2: Int
}

構造体を作ったら、実際にAPIを叩いてレスポンスをUserインスタンスに変換します。

Alamofire.request("http://localhost:3000/users/1.json").response(completionHandler: { response in
    if let data = response.data {
        let result = try? JSONDecoder().decode(User.self, from: data)
        print(result)
    }
})

実際の変換処理は下の部分です。
JSONDecoderのdecodeメソッドに構造体とDataを渡すことでUserインスタンスを生成しています。
型やプロパティー名が違う場合は例外が投げられます。

let result = try? JSONDecoder().decode(User.self, from: data)
print(result)

複数のオブジェクトが返る時はArrayを使います。

Alamofire.request("http://localhost:3000/users.json").response(completionHandler: { response in
    if let data = response.data {
        let result = try? JSONDecoder().decode(Array<User>.self, from: data)
        print(result)
    }
})

数値型はIntではなくDoubleで受け取る事もできます。

struct User: Codable {
    let key1: String
    let key2: Double
}

しかし数値を文字列にしようとすると変換失敗します。

struct User: Codable {
    let key1: String
    let key2: String
}

スネークケースからキャメルケースの変換は自動では行ってくれません。
それとcreated_atのDate型への変換も自動ではできませんでした。

struct User: Codable {
    let key1: String
    let key2: Int
    let created_at: String // → createdAtでは動かない
}

スネークケースからキャメルケースの変換をしたい場合は下のようにCodingKeyを使った実装が必要です。
ここに変換処理を書けば、createdAtをDate型にする事もできそうです。

struct User: Codable {
    let key1: String
    let key2: Int
    let createdAt: String
    
    enum UserCodingKeys: String, CodingKey {
        case createdAt = "created_at", key1, key2
    }
    
    init(from decoder: Decoder) throws {
        let container = try decoder.container(keyedBy: UserCodingKeys.self)
        key1 = try container.decode(String.self, forKey: .key1)
        key2 = try container.decode(Int.self, forKey: .key2)
        createdAt = try container.decode(String.self, forKey: .createdAt)
    }
}

Codableに準拠したオブジェクトは、JSON形式の文字列にする事も可能です。

if let data = try? JSONEncoder( ).encode(User(key1: "AAA", key2: 123, createdAt: "BBB")) {
    print(String(data: data, encoding: .utf8)) // → Optional("{\"key1\":\"AAA\",\"createdAt\":\"BBB\",\"key2\":123}")
}