Codable - ehrldyd15/Swift_Skills GitHub Wiki

Codable

Codable이란?

Encodable과 Decodable을 합친 것이다.

Encodable은 data를 Encoder에서 변환해주려는 프로토콜로 바꿔주는 것

ex) 모델을 json으로 Encode

Decodable은 data를 원하는 모델로 Decode해주는 것

ex) json을 원하는 모델로 Decode

Codable은 프로토콜이기 때문에 채택을 해야 한다.

struct, enum, class 전부 채택이 가능하다.

예제를 통해 배워보자

struct User {
    enum CodingKeys: String, CodingKey ✅ {
        case id
        case name
        case birth
        case phoneNum = "phone_num"
    }

    let id: Int
    let name: String
    let birth: String
    let phoneNum: String
}

CodingKeys는 json key가 아닌 내가 원하는 이름으로 지정해줄 수 있게 해주는 프로토콜이다.

위 예제에서 실제 json key들이 id, name, birth, phone_num 이지만

내가 원하는 이름은 id, name, birth, phoneNum이다.

만약 json key와 내가 지정하는 이름이 같다면 case에 json key만 작성하면 된다.

Decodable

extension User: Decodable ✅ {
    init(from decoder: Decoder) throws {
        let container = try decoder.container(keyedBy: CodingKeys.self)
        
        id = try container.decode(Int.self, forKey: .id)
        name = try container.decode(String.self, forKey: .name)
        birth = try container.decode(String.self, forKey: .birth)
        phoneNum = try container.decode(String.self, forKey: .phoneNum)
    }
}

Decodable은 프로토콜이니까 User stuct에서 채택해준다.

Decodable을 채택하면 무조건 init을 생성해야한다.

위와 같은 형태는 애플 문서에 나와있는 형식이다.

https://developer.apple.com/documentation/foundation/archives_and_serialization/encoding_and_decoding_custom_types

여튼 Decodable을 채택하고

func decode() {
    let jsonString = """ ✅
                        [
                            {
                                "id": 1,
                                "name": "shark1",
                                "birth": "1999-03-17",
                                "phone_num": "010-2222-3333"
                            },
                            {
                                "id": 2,
                                "name": "shark2",
                                "birth": "1999-03-19",
                                "phone_num": "010-2222-3334"
                            },
                            {
                                "id": 3,
                                "name": "shark3",
                                "birth": "1999-03-20",
                                "phone_num": "010-2222-3353"
                            }
                        ]
                      """
    
    let jsonData = jsonString.data(using: .utf8) ✅
    
    do {
        guard let jsonData = jsonData else { return }
        let dataModel = try JSONDecoder().decode([User].self, from: jsonData) ✅
        
        print(dataModel)
    } catch let error {
        print("에러: \(error)")
    }
}
  • jsonString이라는 임의의 json형태인 string타입의 변수를 생성한다.

  • decode를 하기 위해서는 Data타입이 필요하기 때문에 String타입을 Data로 변환시켜준다.

  • JSONDecoder()을 이용하여 decode를 [User]타입에 맞게 구현하면

    [Codable.User(id: 1, name: "A", birth: "1999-03-17", phoneNum: "010-2222-3333"), Codable.User(id: 2, name: "B", birth: "1999-03-19", phoneNum: "010-2222-3334"), Codable.User(id: 3, name: "C", birth: "1999-03-20", phoneNum: "010-2222-3353")]

User 모델에 맞게 decode가 된다.

Encodable

extension User: Encodable { ✅
    func encode(to encoder: Encoder) throws{
        var container = encoder.container(keyedBy: CodingKeys.self)
        try container.encode(id, forKey: .id)
        try container.encode(name, forKey: .name)
        try container.encode(birth, forKey: .birth)
        try container.encode(phoneNum, forKey: .phoneNum)
    }
}

Encodable은 Decodable의 반대로 하면 된다.

Decodable과 마찬가지로 Encodable을 채택한 후

func encode() {
    let userObject = User(id: 1, name: "A", birth: "2000-03-02", phoneNum: "010-3434-2222") ✅
    
    do {
        let jsonData = try JSONEncoder().encode(userObject) ✅
        let jsonString = String(data: jsonData, encoding: .utf8) ✅
        guard let printJsonString = jsonString else { return }
        
        print(printJsonString)
    } catch let error {
        print("에러: \(error)")
    }
}

decode할 때와는 반대로

  • userObject라는 객체를 만든다.

  • JSONEncoder의 encode메서드를 이용하여 Data타입으로 만든다.

  • String 타입으로 만든다.

그러면 아래와 같은 결과를 얻게된다.

{"id":1,"name":"A","birth":"2000-03-02","phone_num":"010-3434-2222"}

추가로 User struct처럼 타입이 명확한 경우에는

struct User: Codable { ✅
    enum CodingKeys: String,CodingKey {
        case id
        case name
        case birth
        case phoneNum = "phone_num"
    }
    
    let id: Int
    let name: String
    let birth: String
    let phoneNum: String
}

Encodable, Decodable을 따로 채택하지 않고, Codable만 채택해서 축약할 수 있다.

Codable은 typealias Codable = Encodable & Decodable

위에 언급했지만 타입이 명확하게 지정되어 있는 친구들만 가능하다.

struct안에 struct가 있다거나, Any타입이거나, [String:Any] 등 이런 친구들은 곤란하다.

참고자료

https://shark-sea.kr/entry/Swift-Codable-%EC%95%8C%EC%95%84%EB%B3%B4%EA%B8%B0?category=784184