Reference
- 내용전반: Apple문서
많은 프로그래밍 task들은 data를 네트워크 연결을 통해 전달하거나 disk에 저장하고 API나 서비스에 제출하는 것들을 포함합니다
이런 task들은 대게 data를 전송할 때 중간 format
으로 인코딩/디코딩해야 합니다
Swift 표준 라이브러리는 data 인코딩/디코딩을 위한 표준화된 접근법을 정의합니다
당신의 custom type에 Encodable
/Decodable
프로토콜을 채택하여 적용할 수 있습니다
이 프로토콜들을 채택하는 것은 Encoder
/Decoder
프로포콜이 data를 취하여 JSON이나 프로퍼티 리스트(.plist
)같은 외부표현으로 인코딩/디코딩하도록 합니다
(참고로, Codable == Encodable & Decodable
)
Codable한 타입의 프로퍼티만으로 구성된 custom type은 Codable 프로포톨을 선언만 하여도 자동으로 준수됩니다
따라서, custom type을 인코딩/디코딩 가능하도록 만들기 위한 가장 간단한 방법은 이미 Codable한 타입의 프로퍼티만 선언하는 것입니다
(ex. String/Int/Double 같은 표준 타입과 Date/Data/URL 등)
struct Coordinate: Codable {
var latitude: Double
var longitude: Double
}
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?
}
인코딩/디코딩 중 한 방향만 사용한다면 굳이 Codable로 모두 채택하지 않아도 된다
struct Landmark: Encodable {
var name: String
var foundingYear: Int
}
struct Landmark: Decodable {
var name: String
var foundingYear: Int
}
Codable 프로토콜을 채택한 타입은 CodingKeys
라는 특별한 열거형을 nested type으로 선언할 수 있습니다 (Codingkeys 열거형은 CodingKey
프로토콜을 채택합니다)
이 열거형이 존재하면, 각 case들은 인코딩/디코딩할 때 포함되어야 할 프로퍼티들의 리스트 역할을 합니다
🔘 case의 이름은 당신의 custom type의 프로퍼티 이름과 동일해야 합니다
🔘 만약 인스턴스를 인코딩/디코딩할 때 없을 것 같은 프로퍼티에 대한 case는 생략하면 됩니다
대신, 생략된 프로퍼티는 default value를 가져야 합니다
(그래야 당신의 custom type이 Codable 프로토콜 자동 준수가 작동합니다)
🔘 만약 JSON에서의 key가 custom type의 프로퍼티 이름과 일치하지 않는다면,
CodingKeys 열거형에 String raw-value를 정의하여 대체 key를 제공하면 됩니다
당신이 각 case에 할당한 String raw-value는 인코딩/디코딩에서 key로 사용됩니다
(String raw-value : JSON에서의 key name)
case 이름과 raw-value 간의 연계로 custom type이 JSON key name을 그대로 사용하지 않고 Swift API Design Guidelines를 따르도록 만들 수 있게 됩니다
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
}
}
custom type을 그대로 인코딩하지 않고 구조를 변경하여 인코딩하고 싶다면, 인코딩/디코딩 로직을 직접 구현할 수도 있습니다
아래 예제에서는
JSON에서 nested 구조인 additionalInfo
를 풀기 위해 별도의 CodingKey
를 선언합니다
let json = """
{
"latitude": 1.0,
"longitude": 1.0,
"additionalInfo": {
"elevation": 1.0
}
}
"""
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
}
}
🔘 커스텀 디코딩: init(from:) 구현
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)
}
}
🔘 커스텀 인코딩: encode(to:) 구현
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)
}
}