SwiftUI: Codable, Encodable, Decodable
Codable: A type that can convert itself into and out of an external representation.(외부 표현으로 전환할 수 있는 유형.)
typealias Codable = Decodable & Encodable
Codable은 Encodable, Decodable 프로토콜로 이루어진, type alias입니다.
Encodable과 Decodable을 동시에 준수하는 프로토콜과 동일해요.
(Codable = Encodable + Decodable = Encodable & Decodable)
그렇다면 Encodable, Decodable은 뭐냐?
Encodable: A type that can encode itself to an external representation.(외부 표현으로 인코딩할 수 있는 유형.)
protocol Encodable
Decodable: A type that can decode itself from an external representation.(외부 표현에서 스스로를 디코딩할 수 있는 유형.)
protocol Decodable
쉽게 말해 Encodable은 JSON과 같은 외부 표현으로 인코딩할 수 있게 해 주고, Decodable은 반대로 외부 표현을 디코딩할 수 있게 해주는 역할을 합니다.
Encoding
struct Landmark: Codable {
var name: String
var foundingYear: Int
}
위 Landmark 구조체는 Codable을 채택함으로써 인코딩과 디코딩이 가능하게 됩니다.
let landmark = Landmark(name: "ACity", foundingYear: 50)
let encoder = JSONEncoder()
encoder.outputFormatting = .prettyPrinted
do {
let values = try encoder.encode(landmark)
print(String(data: values, encoding: .utf8)!)
} catch {
print(error.localizedDescription)
}
// outputFormatting은 디코딩된 값을 좀 더 보기 좋게 꾸며주는 역할
// default:
// {"name":"ACity","foundingYear":50}
//
// prettyPrinted:
// {
// "name" : "ACity",
// "foundingYear" : 50
// }
Landmark 구조체에서 name, foundingYear를 사용하고 있지만 CodingKeys enumeration을 통해서 Key의 이름을 변경할 수 있어요.
예를 들어, API에서 title, founding_date를 사용한다면 name, foundingYear는 올바른 Key가 아니니 nil 처리가 될 것입니다.
따라서 외부 또는 내부의 Key를 일치시키기 위해 CodingKeys를 사용할 수 있습니다.
struct Landmark: Codable {
var name: String
var foundingYear: Int
enum CodingKeys: String, CodingKey {
case name = "title"
case foundingYear = "founding_date"
}
}
// {
// "title" : "ACity",
// "founding_date" : 50
// }
Decoding
디코딩은 인코딩과 반대로 타입과 함께 인코딩 되어있는 데이터를 입력해 주면 됩니다.
let landmarkData = """
{
"founding_date" : 50,
"title" : "ACity"
}
""".data(using: .utf8)!
do {
let values = try JSONDecoder().decode(Landmark.self, from: landmarkData)
print(values)
} catch {
print(error.localizedDescription)
}
// Landmark(name: "ACity", foundingYear: 50)
Serialize
Codable을 채택하면 내장 데이터 포맷과 사용자 정의 Encoder, Decoder가 제공하는 타입에 대한 따로 코드를 작성하지 않아도 직렬화를 할 수 있어요.
struct LandmarkDetail: Codable {
var stroyCount: Int
var story: String
}
struct Landmark: Codable {
var name: String
var foundingYear: Int
var landmarkDetail: LandmarkDetail
}
// Encoding
let landmark = Landmark(name: "ACity", foundingYear: 50, landmarkDetail: LandmarkDetail(stroyCount: 1, story: "BStory"))
let encoder = JSONEncoder()
encoder.outputFormatting = .prettyPrinted
do {
let values = try encoder.encode(landmark)
print(String(data: values, encoding: .utf8)!)
} catch {
print(error.localizedDescription)
}
// {
// "foundingYear" : 50,
// "landmarkDetail" : {
// "stroyCount" : 1,
// "story" : "BStory"
// },
// "name" : "ACity"
// }
// Decoding
let landmarkData = """
{
"landmarkDetail" : {
"stroyCount" : 1,
"story" : "BStory"
},
"name" : "ACity",
"foundingYear" : 50
}
""".data(using: .utf8)!
do {
let values = try JSONDecoder().decode(Landmark.self, from: landmarkData)
print(values)
} catch {
print(error.localizedDescription)
}
// Landmark(name: "ACity", foundingYear: 50,
// landmarkDetail: CodableExample.LandmarkDetail(stroyCount: 1, story: "BStory"))
더불어 Array, Dictionary, Optional과 같이 내장되어 있는 함수들도 Codable을 채택해 인코딩 & 디코딩을 할 수 있습니다.