Swift의 Codable, JSON Encoding 및 Decoding

  • Codable API는 개발자가 JSON 같은 데이터를 쉽게 encode 및 decode 할 수 있게 해준다.
  • 사실, CodableEncodableDecodable Protocol의 type alias이다.

자동화

  • Encodable이든 Decodable을 채택하면 자동으로 필요한 stored property를 encode 혹은 decode 한다.
struct User: Codable {
    var name: String
    var age: Int
}

Encode

  • 다음과 같이 JSONEncoder를 통해 JSON 데이터로 쉽게 encode 할 수 있게 된다.
do {
    let user = User(name: "Hohyeon", age: 22)
    let encoder = JSONEncoder()
    let data = try encoder.encode(user)
} catch {
    print("Whoops, I did it again: \(error)")
}

Decode

  • 다음과 같이 JSONDecoder를 통해 JSON 데이터를 쉽게 decode 할 수도 있다.
  • 다음 코드를 실행하면 정확히 우리가 넣었던 JSON 데이터가 나올 것이다.
let decoder = JSONDecoder()
let secondUser = try decoder.decode(User.self, from: data)

문제점

  • JSON 데이터 형식이 개발자가 원하는 형식이면 상관 없지만 그렇지 않으면 문제가 있다.
  • 다음과 같은 JSON 코드를 decode 하려고 했다고 생각해보자.
  • 위의 코드를 사용해 자동으로 encode 및 decode 할 수 없을 것이다.
{
    "user_data": {
        "full_name": "Hohyeon Moon",
        "user_age": 22
    }
}

해결법

  • 여러 해결 방법이 있지만, 그 중에서 제일 간단하고 효율적인 방법에 대해 알아보겠다.
  • User 구조체를 확장해, Codable을 채택한 CodingData 구조체를 만든다.
  • 그리고 그 안에 다시 Codable을 채택한 Container 구조체를 만든다.
  • Container 안에 JSON 데이터의 camelCase 변수명을 만든다.
extension User {
    struct CodingData: Codable {
        struct Container: Codable {
            var fullName: String
            var userAge: Int
        }

        var userData: Container
    }
}
  • 그리고 다음과 같이 User.CodingData 타입을 확장해 User 인스턴스에 값을 넣어준다.
extension User.CodingData {
    var user: User {
        return User(
            name: userData.fullName,
            age: userData.userAge
        )
    }
}
  • snake_case를 camelCase로 변경하기 위해 convertFromSnakeCase를 사용한다.
  • 직접 decode 하는 대신, User.CodingData의 인스턴스를 decode 하고 그것을 user로 convert 한다.
let decoder = JSONDecoder()
decoder.keyDecodingStrategy = .convertFromSnakeCase

let codingData = try decoder.decode(User.CodingData.self, from: data)
let user = codingData.user

관련 글