Encoding and Decoding Custom Types πŸ“™

YaR LabΒ·2023λ…„ 6μ›” 27일
0

swiftΒ πŸ“™

λͺ©λ‘ 보기
3/16
post-thumbnail

1️⃣ μ„€λͺ…

μ™ΈλΆ€ ν‘œν˜„ ν˜•μ‹(JSON, λ“±)에 ν˜Έν™˜λ˜λ„λ‘ 데이터 μœ ν˜•μ„ 인코딩 및 λ””μ½”λ”©ν•  수 μžˆλ„λ‘ λ§Œλ“€κΈ°

  • 데이터λ₯Ό λ„€νŠΈμ›Œν¬ 연결을 톡해 μ „μ†‘ν•˜κ±°λ‚˜ 데이터λ₯Ό λ””μŠ€ν¬μ— μ €μž₯ν•˜κ±°λ‚˜ API 및 μ„œλΉ„μŠ€μ— 데이터λ₯Ό μ œμΆœν•˜λŠ” 것은 λ§Žμ€ ν”„λ‘œκ·Έλž˜λ° μž‘μ—…μ— 포함 돼있음
  • μ΄λŸ¬ν•œ μž‘μ—…μ€ 데이터가 μ „μ†‘λ˜λŠ” λ™μ•ˆ 쀑간 ν˜•μ‹μœΌλ‘œ 데이터λ₯Ό μΈμ½”λ”©ν•˜κ³  λ””μ½”λ”©ν•΄μ•Ό ν•˜λŠ” κ²½μš°κ°€ 많음
  • Swift ν‘œμ€€ λΌμ΄λΈŒλŸ¬λ¦¬λŠ” 데이터 인코딩과 디코딩에 λŒ€ν•œ ν‘œμ€€ν™”λœ μ ‘κ·Ό 방식을 μ •μ˜ν•¨
  • μ‚¬μš©μž μ •μ˜ νƒ€μž…μ— Encodable 및 Decodableν”„λ‘œν† μ½œμ„ κ΅¬ν˜„ν•¨μœΌλ‘œμ¨ 이 μ ‘κ·Ό 방식을 채택할 수 있음
  • 이 ν”„λ‘œν† μ½œλ“€μ„ μ±„νƒν•¨μœΌλ‘œμ¨ 데이터λ₯Ό μΈμ½”λ”©ν•˜κ±°λ‚˜ λ””μ½”λ”©ν•˜λŠ” μž‘μ—…μ„ μˆ˜ν–‰ν•  수 μžˆλŠ” Encoder와 Decoderκ΅¬ν˜„μ²΄λ“€μ΄ 데이터λ₯Ό κ°€μ Έμ™€μ„œ JSON λ˜λŠ” ν”„λ‘œνΌν‹° λ¦¬μŠ€νŠΈμ™€ 같은 μ™ΈλΆ€ ν‘œν˜„ ν˜•μ‹μœΌλ‘œ μΈμ½”λ”©ν•˜κ±°λ‚˜ λ””μ½”λ”©ν•  수 있음

    ν”„λ‘œνΌν‹° 리슀트

    • λ‹€μ–‘ν•œ 데이터 μœ ν˜•μ„ μ§€μ›ν•˜λŠ” 계측적인 ꡬ쑰
    • 닀은은 μ•±μ˜ ꡬ성 섀정을 μ €μž₯ν•˜κΈ° μœ„ν•œ ν”„λ‘œνΌν‹° 리슀트
    <dict>
       <key>Theme</key>
       <string>Dark</string>
       <key>Language</key>
       <string>English</string>
       <key>Notifications</key>
       <true/>
    </dict>
  • 인코딩과 디코딩을 λͺ¨λ‘ μ§€μ›ν•˜λ €λ©΄ Encodableκ³Ό Decodableν”„λ‘œν† μ½œμ„ κ²°ν•©ν•œ Codable을 μ±„νƒν•˜λ„λ‘ μ„ μ–Έ
  • 이 과정은 νƒ€μž…μ„ Codableν•˜κ²Œ λ§Œλ“¬

πŸ“Œ Encode and Decode Automatically

  • νƒ€μž…μ„ Codable둜 λ§Œλ“œλŠ” κ°€μž₯ κ°„λ‹¨ν•œ 방법은 ν•΄λ‹Ή νƒ€μž…μ˜ 속성듀을 이미 Codable인 νƒ€μž…μœΌλ‘œ μ„ μ–Έν•˜λŠ” 것
  • μ΄λŸ¬ν•œ νƒ€μž…λ“€μ€ String, Int, Doubleκ³Ό 같은 ν‘œμ€€ 라이브러리 νƒ€μž…, 그리고 Date, Data, URLκ³Ό 같은 Foundation νƒ€μž…μ„ 포함
  • 속성듀이 Codable인 μ–΄λ–€ νƒ€μž…μ΄λ“  간에, ν•΄λ‹Ή νƒ€μž…μ΄ Codable을 μ€€μˆ˜ν•œλ‹€λŠ” μ„ μ–Έλ§Œ ν•˜λ©΄ μžλ™μœΌλ‘œ Codable에 μ€€μˆ˜ν•˜κ²Œ 됨
  • μ•„λž˜ μ˜ˆμ œλŠ” λžœλ“œλ§ˆν¬(Landmark) ꡬ쑰체의 μ˜ˆμ‹œμ΄κ³  κ΅¬μ‘°μ²΄λŠ” λžœλ“œλ§ˆν¬μ˜ 이름과 창립 연도λ₯Ό μ €μž₯함
struct Landmark {
    var name: String
    var foundingYear: Int
}
  • Landmarkꡬ쑰체에 Codable을 상속 λͺ©λ‘μ— μΆ”κ°€ν•˜λ©΄, Encodableκ³Ό Decodable의 λͺ¨λ“  ν”„λ‘œν† μ½œ μš”κ΅¬μ‚¬ν•­μ„ μΆ©μ‘±ν•˜κ²Œ 됨
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을 μ±„νƒν•˜λ©΄ λ‚΄μž₯된 데이터 ν˜•μ‹ 및 μ‚¬μš©μž μ •μ˜ 인코더 및 디코더가 μ œκ³΅ν•˜λŠ” ν˜•μ‹κ³Όμ˜ 직렬화 및 역직렬화가 κ°€λŠ₯해짐
  • Landmarkκ΅¬μ‘°μ²΄λŠ” PropertyListEncoder 및 JSONEncoder클래슀λ₯Ό μ‚¬μš©ν•˜μ—¬ 인코딩이 κ°€λŠ₯해짐
  • Landmarkμžμ²΄μ—λŠ” λͺ…μ‹œμ μœΌλ‘œ ν”„λ‘œνΌν‹° λ¦¬μŠ€νŠΈλ‚˜ JSON을 μ²˜λ¦¬ν•˜λŠ” μ½”λ“œκ°€ 없더라도 κ°€λŠ₯
  • 이 원칙은 Codable둜 κ΅¬μ„±λœ λ‹€λ₯Έ μ‚¬μš©μž μ •μ˜ νƒ€μž…μ—λ„ 적용됨
  • λͺ¨λ“  ν”„λ‘œνΌν‹°κ°€ Codable을 μ€€μˆ˜ν•œλ‹€λ©΄, μ‚¬μš©μž μ •μ˜ νƒ€μž…λ„ Codable이 될 수 있음
  • μ•„λž˜ 예제 μ—μ„œλŠ” Landmarkꡬ쑰체에 locationν”„λ‘œνΌν‹°λ₯Ό μΆ”κ°€ν–ˆμ„ λ•Œ μžλ™ 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, Dictionary, Optionalκ³Ό 같은 Built-in types은 Codable을 μ€€μˆ˜ν•¨
  • Built-in types이 Codableνƒ€μž…μ„ ν¬ν•¨ν•˜κ³  μžˆμ„ λ•Œ μžλ™μœΌλ‘œ Codable을 μ€€μˆ˜ν•˜κ²Œ 됨
  • Landmark에 CoordinateμΈμŠ€ν„΄μŠ€μ˜ 배열을 좔가해도 전체 κ΅¬μ‘°μ²΄λŠ” μ—¬μ „νžˆ Codable을 μ€€μˆ˜ν•¨
  • μ•„λž˜ μ˜ˆμ œλŠ” Landmark내에 λ‚΄μž₯된 Codableνƒ€μž…μ„ μ‚¬μš©ν•˜μ—¬ μ—¬λŸ¬ ν”„λ‘œνΌν‹°λ₯Ό μΆ”κ°€ν–ˆμ„ λ•Œ μžλ™ 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μœ ν˜•μ€ CodingKeyν”„λ‘œν† μ½œμ„ μ€€μˆ˜ν•˜λŠ” νŠΉμˆ˜ν•œ 쀑첩 μ—΄κ±°ν˜•μΈ CodingKeysλ₯Ό μ„ μ–Έν•  수 있음
  • 이 μ—΄κ±°ν˜•μ΄ μ‘΄μž¬ν•  경우, ν•΄λ‹Ή 사둀듀은 인코딩 λ˜λŠ” λ””μ½”λ”©ν•  λ•Œ ν¬ν•¨λ˜μ–΄μ•Ό ν•˜λŠ” μ†μ„±λ“€μ˜ 정식 λͺ©λ‘μœΌλ‘œ μž‘λ™
  • μ—΄κ±°ν˜• case의 이름은 μœ ν˜•μ˜ ν•΄λ‹Ή property에 μ§€μ •ν•œ 이름과 μΌμΉ˜ν•΄μ•Ό 함
  • DecodingμΈμŠ€ν„΄μŠ€μ— 없을 경우 λ˜λŠ” μΈμ½”λ”©λœ ν‘œν˜„μ— νŠΉμ • 속성을 ν¬ν•¨μ‹œν‚€μ§€ μ•Šμ•„μ•Όν•˜λŠ” 경우 CodingKeysμ—΄κ±°ν˜•μ—μ„œ 속성을 μ œμ™Έμ‹œμΌœμ•Ό 함
  • CodingKeysμ—μ„œ μ œμ™Έλœ 속성은 Decodable λ˜λŠ” Codable에 λŒ€ν•œ μžλ™ μ€€μˆ˜λ₯Ό λ°›κΈ° μœ„ν•΄ 기본값이 ν•„μš”ν•¨
  • μ§λ ¬ν™”λœ 데이터 ν˜•μ‹(JSON, λ“±)μ—μ„œ μ‚¬μš©ν•˜λŠ” ν‚€κ°€ 데이터 μœ ν˜•μ˜ 속성 이름과 μΌμΉ˜ν•˜μ§€ μ•ŠλŠ” 경우 CodingKeysμ—΄κ±°ν˜•μ˜ rawValue둜 String을 μ§€μ •ν•˜μ—¬ λŒ€μ²΄ ν‚€λ₯Ό μ œκ³΅ν•  수 있음
  • 각 μ—΄κ±°ν˜• case의 rawValue둜 μ‚¬μš©ν•˜λŠ” λ¬Έμžμ—΄μ€ 인코딩 및 λ””μ½”λ”© 쀑에 μ‚¬μš©λ˜λŠ” ν‚€ 이름
  • case이름과 rawValue κ°„μ˜ μ—°κ΄€ 관계λ₯Ό 톡해 데이터 ꡬ쑰λ₯Ό Swift API Design Guidelines에 따라 λͺ…λͺ…ν•  수 있으며 μ§λ ¬ν™”λœ 데이터(JSON, λ“±)의 이름, ꡬ두점 및 λŒ€λ¬Έμžμ™€ μΌμΉ˜μ‹œν‚¬ ν•„μš”κ°€ 없어짐
  • μ•„λž˜ μ˜ˆμ œμ—μ„œλŠ” 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 νƒ€μž…μ˜ ꡬ쑰가 μΈμ½”λ”©λœ ν˜•μ‹μ˜ ꡬ쑰와 λ‹€λ₯Έ 경우, 직접 Encodableκ³Ό Decodable을 κ΅¬ν˜„ν•˜μ—¬ μ‚¬μš©μž μ •μ˜ 인코딩 및 λ””μ½”λ”© λ‘œμ§μ„ μ •μ˜ν•  수 있음
  • μ•„λž˜ μ˜ˆμ‹œμ—μ„œλŠ” 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 νƒ€μž…μ˜ μΈμ½”λ”©λœ ν˜•μ‹μ€ 두 번째 μˆ˜μ€€μ˜ 쀑첩 정보λ₯Ό ν¬ν•¨ν•˜κ³  있기 λ•Œλ¬Έμ— ν•΄λ‹Ή νƒ€μž…μ€ Encodable 및 Decodableν”„λ‘œν† μ½œμ„ μ±„νƒν•˜λŠ” 데 두 개의 μ—΄κ±°ν˜•μ„ μ‚¬μš©

    두 번째 μˆ˜μ€€μ˜ 쀑첩 정보(second level of nested information)

    • Coordinateνƒ€μž…μ˜ μΈμ½”λ”©λœ ν˜•μ‹μ—μ„œ μΆ”κ°€λ‘œ ν¬ν•¨λ˜λŠ” λ°μ΄ν„°μ˜ ꡬ쑰λ₯Ό 의미
    • 예λ₯Ό λ“€μ–΄ JSONμ—μ„œ additionalInfo객체 μ•ˆμ— elevation이 포함 된 ꡬ쑰
  • 각각의 μ—΄κ±°ν˜•μ€ 인코딩 λ˜λŠ” λ””μ½”λ”© μž‘μ—…μ„ μˆ˜ν–‰ν•˜λŠ” νŠΉμ • μˆ˜μ€€μ—μ„œ μ‚¬μš©λ˜λŠ” λͺ¨λ“  μ½”λ”© ν‚€(속성 이름)의 λͺ©λ‘μ„ 포함
  • μ•„λž˜ 예제 μ—μ„œλŠ” Coordinateꡬ쑰체λ₯Ό ν™•μž₯ν•˜μ—¬ Decodableν”„λ‘œν† μ½œμ„ μ€€μˆ˜ν•˜κΈ° μœ„ν•΄ ν•„μš”ν•œ μ΄λ‹ˆμ…œλΌμ΄μ €μΈ 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)
    }
}
  • init(from:)λ©”μ„œλ“œλŠ” Decoderλ₯Ό λ§€κ°œλ³€μˆ˜λ‘œ λ°›κ³ , λ””μ½”λ”© μž‘μ—…μ„ μˆ˜ν–‰
  • λ©”μ„œλ“œ λ‚΄λΆ€μ—μ„œλŠ” decoder.container(keyedBy:)λ©”μ„œλ“œλ₯Ό μ‚¬μš©ν•˜μ—¬ λ””μ½”λ”μ˜ μ»¨ν…Œμ΄λ„ˆλ₯Ό μ–»μŒ
  • 이 μ»¨ν…Œμ΄λ„ˆλŠ” λ””μ½”λ”©ν•  데이터에 λŒ€ν•œ ν‚€-κ°’ μŒμ„ 포함
  • container(keyedBy:)λ©”μ„œλ“œλ₯Ό μ‚¬μš©ν•˜μ—¬ valuesλ³€μˆ˜μ— λŒ€ν•œ μ»¨ν…Œμ΄λ„ˆλ₯Ό 생성
  • valuesμ»¨ν…Œμ΄λ„ˆλŠ” CodingKeysμ—΄κ±°ν˜•μ„ ν‚€ νƒ€μž…μœΌλ‘œ μ‚¬μš©ν•˜λ©°, ν•΄λ‹Ή μ—΄κ±°ν˜•μ€ latitude와 longitude속성에 λŒ€μ‘ν•˜λŠ” μ½”λ”© ν‚€λ₯Ό λ‚˜νƒ€λƒ„
  • decode(_:forKey:)λ©”μ„œλ“œλ₯Ό μ‚¬μš©ν•˜μ—¬ μ»¨ν…Œμ΄λ„ˆμ—μ„œ 각 μ†μ„±μ˜ 값을 λ””μ½”λ”©ν•˜κ³ , 이λ₯Ό latitude와 longitude속성에 ν• λ‹Ή
  • nestedContainer(keyedBy:forKey:)λ©”μ„œλ“œλ₯Ό μ‚¬μš©ν•˜μ—¬ additionalInfoλ³€μˆ˜μ— λŒ€ν•œ μ€‘μ²©λœ μ»¨ν…Œμ΄λ„ˆλ₯Ό 생성
  • 이 μ»¨ν…Œμ΄λ„ˆλŠ” AdditionalInfoKeysμ—΄κ±°ν˜•μ„ ν‚€ νƒ€μž…μœΌλ‘œ μ‚¬μš©ν•˜λ©°, ν•΄λ‹Ή μ—΄κ±°ν˜•μ€ elevation속성에 λŒ€μ‘ν•˜λŠ” μ½”λ”© ν‚€λ₯Ό λ‚˜νƒ€λƒ„
  • decode(_:forKey:)λ©”μ„œλ“œλ₯Ό μ‚¬μš©ν•˜μ—¬ μ€‘μ²©λœ μ»¨ν…Œμ΄λ„ˆμ—μ„œ elevationμ†μ„±μ˜ 값을 λ””μ½”λ”©ν•˜κ³ , 이λ₯Ό elevation속성에 ν• λ‹Ή
  • μ΄λ ‡κ²Œ ν•΄μ„œ init(from:)λ©”μ„œλ“œλŠ” λ””μ½”λ”© μž‘μ—…μ„ μ™„λ£Œν•˜κ³ , Coordinateꡬ쑰체의 λͺ¨λ“  속성이 μ˜¬λ°”λ₯΄κ²Œ μ΄ˆκΈ°ν™”λœ μƒνƒœλ‘œ μΈμŠ€ν„΄μŠ€λ₯Ό 생성함

    μ»¨ν…Œμ΄λ„ˆ

    • μ»¨ν…Œμ΄λ„ˆλŠ” λ””μ½”λ”© μž‘μ—…μ—μ„œ μ‚¬μš©λ˜λŠ” ꡬ쑰체둜, λ””μ½”λ”©ν•  데이터에 λŒ€ν•œ ν‚€-κ°’ μŒμ΄λ‚˜ 순차적인 값듀을 λ‹΄κ³  있음
    • KeyedDecodingContainer λ˜λŠ” UnkeyedDecodingContainerν”„λ‘œν† μ½œμ„ μ€€μˆ˜ν•˜λŠ” μΈμŠ€ν„΄μŠ€
  • CoordinateμΈμŠ€ν„΄μŠ€μ˜ 두 속성인 latitude 와 longitude은 Swift ν‘œμ€€ λΌμ΄λΈŒλŸ¬λ¦¬μ—μ„œ μ œκ³΅ν•˜λŠ” KeyedContainer APIλ₯Ό μ‚¬μš©ν•˜μ—¬ μ΄ˆκΈ°ν™”λ¨
  • 이 APIλŠ” λ””μ½”λ”© μž‘μ—… 쀑에 μ‚¬μš©λ˜λ©°, λ””μ½”λ”λ‘œλΆ€ν„° 전달받은 DecoderμΈμŠ€ν„΄μŠ€λ₯Ό 톡해 μ ‘κ·Όν•  수 있음
  • μ•„λž˜ μ˜ˆμ œλŠ” Coordinateꡬ쑰체가 Encodableν”„λ‘œν† μ½œμ„ μ€€μˆ˜ν•˜κΈ° μœ„ν•΄ ν•„μš”ν•œ λ©”μ„œλ“œμΈ 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)
    }
}
  • 이 encode(to:)λ©”μ„œλ“œμ˜ κ΅¬ν˜„μ€ 이전 예제의 λ””μ½”λ”© μž‘μ—…μ„ μ—­μœΌλ‘œ μˆ˜ν–‰

μΆœμ²˜πŸ“š

🍎Apple Docs: Encoding and Decoding Custom Types

0개의 λŒ“κΈ€