Swift

SwiftUI: Codable, Encodable, Decodable

songmoro 2023. 9. 15. 12:13
728x90

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을 채택해 인코딩 & 디코딩을 할 수 있습니다.

 


참고

Encoding and Decoding Custom Types

728x90