본문 바로가기

swift

[Swift] Codable 이해하기

enum Block: Codable {
    case text(TextBlock)
    case image(ImageBlock)
    case video(VideoBlock)

    enum CodingKeys: String, CodingKey {
        case type
    }

    enum BlockType: String, Codable {
        case text
        case image
        case video
    }

    init(from decoder: Decoder) throws {
        let container = try decoder.container(keyedBy: CodingKeys.self)
        let type = try container.decode(BlockType.self, forKey: .type)

        let singleValue = try decoder.singleValueContainer()

        switch type {
        case .text:
            let text = try singleValue.decode(TextBlock.self)
            self = .text(text)
        case .image:
            let image = try singleValue.decode(ImageBlock.self)
            self = .image(image)
        case .video:
            let video = try singleValue.decode(VideoBlock.self)
            self = .video(video)
        }
    }

    func encode(to encoder: Encoder) throws {
        switch self {
        case .text(let block):
            try block.encode(to: encoder)
        case .image(let block):
            try block.encode(to: encoder)
        case .video(let block):
            try block.encode(to: encoder)
        }
    }
}

 

Codable

  • 자료를 쉽게 저장하고 불러올수있도록 해준다.
  • Json 파일을 읽거나 저장하도록 도와준다.
  • Codable을 붙이면 자동으로 파일에 저장하거나 파일에서 불러올수있게 해줌

Enum - case text(TextBlock)

  • text라는 case에 TextBlock 이라는 추가정보가 있는 것임
  • text를 선택할 때 TextBlock이라는 추가정보가 있어야함

CodingKey

enum CodingKeys: String, CodingKey {
    case type
}

init(from decoder: Decoder) throws {
        let container = try decoder.container(keyedBy: CodingKeys.self)
        let type = try container.decode(BlockType.self, forKey: .type)

        let singleValue = try decoder.singleValueContainer()

        switch type {
        case .text:
            let text = try singleValue.decode(TextBlock.self)
            self = .text(text)
        case .image:
            let image = try singleValue.decode(ImageBlock.self)
            self = .image(image)
        case .video:
            let video = try singleValue.decode(VideoBlock.self)
            self = .video(video)
        }
    }
    
    func encode(to encoder: Encoder) throws {
        switch self {
        case .text(let block):
            try block.encode(to: encoder)
        case .image(let block):
            try block.encode(to: encoder)
        case .video(let block):
            try block.encode(to: encoder)
        }
    }
  • json 파일이나 데이터에서 type이라는 이름으로 저장되어있다고 알려주는 것
  • 이 코드에서는 저장할 때 다른 타입과 값을 저장하기 위해서 init(from:)과 func encode를 구현함 
  • container(keyedBy:) 쓸 때는, 거기에 넘기는 타입이 반드시 CodingKey 프로토콜을 따라야 한다!

 

///json 값
{
  "type": "text"
}

struct Block: Codable {
    let blockType: String  // 변수 이름이 'type'이 아님

    enum CodingKeys: String, CodingKey {
        case blockType = "type" // JSON의 "type"을 blockType 변수에 매칭
    }
}
  • CodingKey는 json의 키 이름과 다르게 저장하고싶을 때 사용된다.
  • container(keyedBy:) 쓸 때는, 거기에 넘기는 타입이 반드시 CodingKey 프로토콜을 따라야 한다!

Decoder

let container = try decoder.container(keyedBy: CodingKeys.self)
  • decorder
    • 데이터나 파일을 코드 안의 변수로 바꿔주는 도구
    • decorder는 데이터를 읽는 기계
let container = try decoder.container(keyedBy: CodingKeys.self)
  • json 안에서 key-value를 꺼내올 준비를 한다.
  • CodingKeys에 있는 키와 값을 꺼낼 준비를 한다.

 

decoder.singleValueContainer()
  • TextBlock이나 ImageBlock 같은 딱하나 있는 덩어리를 꺼내기 위함

 

let text = try singleValue.decode(TextBlock.self)

 

  • 아까 만든 singleValueContainer에서 TextBlock 타입으로 데이터를 꺼내는 거야.
  • 즉, 저장돼 있던 JSON 데이터를 읽어서 TextBlock이라는 진짜 객체로 복구하는 거지.

 

Encoder

try block.encode(to: encoder)

 

  • 변수를 파일이나 데이터로 저장
  • encode(to:)는 자동으로 데이터를 json파일로 저장함

 

호출하는 법

let block = try JSONDecoder().decode(Block.self, from: jsonData)
  • init(from:)을 해주지 않아도 Block.self를 썼으니 Swift에서 알아서 내가 커스텀한 init(from:)을 호출한다.