Encoding and Decoding Custom Types

YesCoach·2021년 7월 6일
0

Encoding and Decoding Custom Types 공식문서

JSON과 같은 외부 표현과의 호환성을 위해 데이터 타입을 인코딩 및 디코딩할 수 있도록 합니다.

Overview

대부분의 프로그래밍 작업에는 네트워크 연결을 통해 데이터를 전송하거나, 디스크에 데이터를 저장하거나, API 및 서비스에 데이터를 제출하는 작업이 포함됩니다. 이러한 작업에서는 데이터가 전송되는 동안 중간 형식 과 주고 받는 형식으로 데이터를 인코딩하고 디코딩해야 하는 경우가 많습니다.

Swift 표준 라이브러리는 데이터 인코딩 및 디코딩에 대한 표준화된 접근 방식을 정의합니다. 사용자 지정 타입에 대해 인코딩 및 디코딩 프로토콜을 구현하여 이 방식을 채택합니다. 이러한 프로토콜을 채택하면 Encoder 및 Decoder 프로토콜을 구현하여 데이터를 가져와서 JSON 또는 프로퍼티 리스트와 같은 외부 표현으로 인코딩하거나 디코딩할 수 있습니다. 인코딩과 디코딩을 모두 지원하려면 Encodable 프로토콜과 Decodable 프로토콜을 결합한 Codable에 대한 준수를 선언하세요. 이 프로세스를 통해 타입을 Codable할 수 있습니다.

Encode and Decode Automatically

타입을 Codable 할 수 있는 가장 간단한 방법은 이미 Codable 할 수 있는 타입을 사용하여 프로퍼티을 선언하는 것입니다. 이러한 타입에는 String, Int 및 Double과 같은 표준 라이브러리 타입과 Date, Data 및 URL과 같은 Foundation 타입이 포함됩니다. 프로퍼티를 Codable 할 수 있는 모든 타입은 해당 준수를 선언하는 것만으로 Codable를 준수 할 수 있습니다.

랜드마크의 명칭과 설립 연도를 저장하는 랜드마크 구조를 고려해 보세요.

struct LandMark {
    var name: String
    var foundingYear: Int
}

Codable을 Landmark의 상속 목록에 추가하면 Encodable 및 Decodable의 모든 프로토콜 요구 사항을 충족하는 자동 준수(automatic conformance)가 이루어집니다.

struct Landmark: Codable {
    var name: String
    var foundingYear: Int
    
    // Landmark now supports the Codable methods init(from:) and encode(to:), 
    // even though they aren't written as part of its declaration.
}

자신의 타입에 따라 Codable을 채택하면 내장(built-in) 데이터 포맷과 사용자 지정(custom) 인코더와 디코더가 제공하는 포맷을 직렬화(Serialize)할 수 있습니다. 예를 들어 Landmark 자체에 프로퍼티 리스트 또는 JSON을 구체적으로 처리하는 코드가 포함되어 있지 않더라도, Landmark 타입은 PropertyListEncoderJSONEncoder 클래스를 사용하여 인코딩할 수 있습니다.

Codeble 할 수 있는 다른 사용자 정의 타입으로 구성된 사용자 정의 타입에도 동일한 원칙이 적용됩니다. 모든 프로퍼티가 Codable 한 경우, 모든 사용자 정의 타입도 Codable 할 수 있습니다.

아래 예시는 위치 프로퍼티가 Landmark 타입에 추가될 때, Codable 자동 준수가 어떻게 적용되는지를 보여줍니다.

struct Coordinate: Codable {
    var latitude: Double
    var longitude: Double
}

struct Landmark: Codable {
    // Double, String, and Int all conform to Codable.
    var name: String
    var foundingYear: Int
    
    // Adding a property of a custom Codable type maintains overall Codable conformance.
    var location: Coordinate
}

Array, DictionaryOptional과 같은 내장 타입(Built-in types)도 Codable 타입을 포함할 때마다 Codable을 준수합니다. Coordinate 인스턴스 배열을 Landmark에 추가할 수 있지만, 전체 구조는 여전히 Codable을 충족합니다.

아래 예시는 Landmark에 내장된 Codable 타입을 사용하여 여러 프로퍼티를 추가할 때 자동 준수가 어떻게 적용되는지를 보여줍니다.

struct Landmark: Codable {
    var name: String
    var foundingYear: Int
    var location: Coordinate
    
    // Landmark is still codable after adding these properties.
    var vantagePoints: [Coordinate]
    var metadata: [String: String]
    var website: URL?
}

Encode or Decode Exclusively

경우에 따라 양방향 인코딩 및 디코딩을 위한 Codable의 지원이 필요하지 않을 수 있습니다. 예를 들어, 일부 앱은 원격 네트워크 API에만 호출하면 되며 동일한 타입의 응답을 디코딩할 필요가 없습니다. 데이터 인코딩만 지원하면 되는 경우 Encodable 준수를 선언합니다. 반대로 특정 타입의 데이터만 읽으면 되는 경우 Decodable 준수를 선언하세요.

아래의 예시는 데이터만 인코딩 또는 디코딩하는 Landmark 타입의 대체 선언을 보여줍니다.

struct Landmark: Encodable {
    var name: String
    var foundingYear: Int
}
struct Landmark: Decodable {
    var name: String
    var foundingYear: Int
}

Choose Properties to Encode and Decode Using Coding Keys

Codable 타입은 CodingKeys 프로토콜에 부합하는 CodingKeys라는 이름의 특별한 중첩 열거형를 선언할 수 있습니다. 이 열거형이 있으면 해당 케이스는 Codable 타입의 인스턴스가 인코딩 또는 디코딩될 때 포함되어야 하는 권한 있는 프로퍼티들의 리스트 역할을 합니다.
열거형 케이스의 이름해당 타입의 프로퍼티에 지정한 이름과 일치해야 합니다.

인스턴스를 디코딩할 때 프로퍼티가 표시되지 않거나, 특정 프로퍼티를 인코딩된 표현에 포함하면 안 되는 경우 CodingKeys 열거형에서 해당 프로퍼티를 생략합니다. CodingKeys에서 생략된 프로퍼티는 포함된 타입이 Decodable 또는 Codable에 대한 자동 준수를 하려면 기본값이 필요합니다.

직렬화된 데이터 포맷에 사용된 키데이터 타입의 속성 이름과 일치하지 않는 경우, CodingKeys 열거형에 대한 원시 값 타입으로 문자열을 지정하여 대체 키를 제공합니다. 각 열거형 케이스의 원시 값으로 사용하는 문자열은 인코딩 및 디코딩 시 사용되는 키 이름입니다. 케이스 이름과 원시 값 간의 연결을 통해 모델링 중인 일련화 포맷의 이름, 구두점 및 대문자를 일치시킬 필요 없이 Swift API 설계 지침에 따라 데이터 구조의 이름을 지정할 수 있습니다.

아래 예시에서는 인코딩 및 디코딩 시 Landmark 타입의 name및 foundingYear 프로퍼티에 대체 키를 사용합니다.

struct Landmark: Codable {
    var name: String
    var foundingYear: Int
    var location: Coordinate
    var vantagePoints: [Coordinate]
    
    enum CodingKeys: String, CodingKey {
        case name = "title"
        case foundingYear = "founding_date"
        
        case location
        case vantagePoints
    }
}

Encode and Decode Manually

Swift 타입의 구조가 인코딩된 양식의 구조와 다를 경우 인코딩 및 디코딩 로직을 정의하기 위한 사용자 정의 구현을 제공할 수 있습니다.

아래 예시에서 Coordinate는 additionalInfo 컨테이너 내부에 중첩된 elevation 프로퍼티를 지원하도록 확장됩니다.

struct Coordinate {
    var latitude: Double
    var longitude: Double
    var elevation: Double

    enum CodingKeys: String, CodingKey {
        case latitude
        case longitude
        case additionalInfo
    }
    
    enum AdditionalInfoKeys: String, CodingKey {
        case elevation
    }
}

코드화된 Coordinate 타입의 형태는 second level의 중첩된 정보를 포함하므로, 이 타입의 EncodableDecodable 프로토콜은 각각 특정 레벨에서 사용되는 Coding Keys가 포함된 두 개의 열거형을 사용합니다.

아래 예시에서, Coordinate 타입은 필요한 이니셜라이저 init(init: from:)를 구현함으로써 Decodable 프로토콜에 부합하도록 확장됩니다.

extension Coordinate: Decodable {
    init(from decoder: Decoder) throws {
        let values = try decoder.container(keyedBy: CodingKeys.self)
        latitude = try values.decode(Double.self, forKey: .latitude)
        longitude = try values.decode(Double.self, forKey: .longitude)
        
        let additionalInfo = try values.nestedContainer(keyedBy: AdditionalInfoKeys.self, forKey: .additionalInfo)
        elevation = try additionalInfo.decode(Double.self, forKey: .elevation)
    }
}

Initializer는 전달받은 Decoder 인스턴스의 메소드를 매개 변수로 사용하여 Coordinate 인스턴스를 채웁니다. Coordinate 인스턴스의 두 개의 프로퍼티는 Swift 표준 라이브러리에서 제공하는 키 컨테이너 API를 사용하여 초기화됩니다.

keyed container APIs...? 찾아보기

아래 예시는 필요한 메소드인 encode(to:):를 구현하여 Encodable 프로토콜에 맞게 Coordinate 타입을 확장할 수 있는 방법을 보여줍니다.

extension Coordinate: Encodable {
    func encode(to encoder: Encoder) throws {
        var container = encoder.container(keyedBy: CodingKeys.self)
        try container.encode(latitude, forKey: .latitude)
        try container.encode(longitude, forKey: .longitude)
        
        var additionalInfo = container.nestedContainer(keyedBy: AdditionalInfoKeys.self, forKey: .additionalInfo)
        try additionalInfo.encode(elevation, forKey: .elevation)
    }
}

이러한 encode(to:) 메서드의 구현은 이전 예제에서 decoding 기능을 반대로 실행합니다.

인코딩 및 디코딩 프로세스를 사용자 지정(customizing) 할 때 사용되는 컨테이너 타입(container type)에 대한 자세한 내용은 KeyedEncoding Container ProtocolUnkeyedEncoding Container를 참조하세요.

미완성

profile
iOS dev / Japanese with Computer Science

0개의 댓글