관리 메뉴

김종권의 iOS 앱 개발 알아가기

[iOS - swift] json, codable, nestedContainer(keyedBy:forKey:), KeyedCodable로 중첩 모델 처리 방법 본문

iOS framework

[iOS - swift] json, codable, nestedContainer(keyedBy:forKey:), KeyedCodable로 중첩 모델 처리 방법

jake-kim 2022. 2. 9. 23:45

* 기본적인 Codable, Decode, Encode 개념은 이곳 참고

* 관련 포스팅 글

중첩 모델을 flatten 모델로 정의 방법

{
  "user_id": "jake",
  "blog_info": {
    "name": {
      "first": "iOS 앱 개발 알아가기",
      "second": "SwiftUI 앱 개발 알아가기"
    }
  }
}

 

  • 이전까지는 blog_info.name.first에 접근하기 위해서 nested되지 않도록 nestedContainer(keyedBy:forKey:)를 사용
  • 이전 포스팅 글 decoding, encoding 참고
    struct MyModel {
      enum CodingKeys: String, CodingKey {
        case userID = "user_id"
        case blogInfo = "blog_info"
      }
      enum BlogInfoKeys: String, CodingKey {
        case name
      }
      enum NameKeys: String, CodingKey {
        case first
        case second
      }
      var userID: String?
      var blogName: String?
    }
    
    extension MyModel: Decodable {
      init(from decoder: Decoder) throws {
        let codingKeys = try decoder.container(keyedBy: CodingKeys.self)
        self.userID = try codingKeys.decode(String.self, forKey: .userID)
        
        let blogInfoKeys = try codingKeys.nestedContainer(keyedBy: BlogInfoKeys.self, forKey: .blogInfo)
        let nameKeys = try blogInfoKeys.nestedContainer(keyedBy: NameKeys.self, forKey: .name)
        self.blogName = try nameKeys.decode(String.self, forKey: .first)
      }
    }
    
    extension MyModel: Encodable {
      func encode(to encoder: Encoder) throws {
        var containerKeys = encoder.container(keyedBy: CodingKeys.self)
        try containerKeys.encode(self.userID, forKey: .userID)
        
        var blogInfoKeys = containerKeys.nestedContainer(keyedBy: BlogInfoKeys.self, forKey: .blogInfo)
        var nameKeys = blogInfoKeys.nestedContainer(keyedBy: NameKeys.self, forKey: .name)
        try nameKeys.encode(self.blogName, forKey: .first)
      }
    }​
  • nestedContainer를 일일이 써주는 것이 복잡하므로 KeyedCodable 프레임워크 사용

KeyedCodable

  • git repo
  • nested key를 쉽게 접근할 수 있는 기능

https://github.com/dgrzeszczak/KeyedCodable

KeyedCodable 사용 방법

  • cocoapods 사용
    pod 'KeyedCodable'​
  • 모델 정의
    • CodingKeys에서 CodingKey대신 KeyedKey 프로토콜 준수
    • nested된 json에 접근할때는 dot. 으로 접근
      import KeyedCodable
      /*
      {
        "user_id": "jake",
        "blog_info": {
          "name": {
            "first": "iOS 앱 개발 알아가기",
            "second": "SwiftUI 앱 개발 알아가기"
          }
        }
      }
       */
       
      struct MyModel: Codable {
        enum CodingKeys: String, KeyedKey {
          case userID = "user_id"
          case blogName = "blog_info.name.first"
        }
        var userID: String?
        var blogName: String?
      }​
    • decode, encode를 사용하는 쪽에서도 매우 편리하게 사용 가능
      import KeyedCodable
        
      let jsonSample =
      """
      {
      "user_id": "jake",
      "blog_info": {
        "name": {
          "first": "iOS 앱 개발 알아가기",
          "second": "SwiftUI 앱 개발 알아가기"
        }
      }
      }
      """
      
      // decoding
      let model = try? MyModel.keyed.fromJSON(self.jsonSample)
      print(model)
      
      // encoding
      let jsonString = try? model?.keyed.jsonString()
      print(jsonString)​
    • cf) KeyedCodable을 사용하지 않고 encode, decode 처리
      // decoding
      guard
        let data = self.jsonSample.data(using: .utf8),
        let myModel = try? JSONDecoder().decode(MyModel.self, from: data)
      else { return }
      print(myModel.blogName)
      
      // encoding
      let sampleMyModel = MyModel(userID: "jake", blogName: "iOS 앱 개발 알아가기")
      guard let data = try? JSONEncoder().encode(sampleMyModel) else { return }
      let jsonString = String(data: data, encoding: .utf8)
      print(jsonString)​

KeyOptions - json 스트링에 dot 키워드가 있는 경우 처리

  • second.example에 접근 방법?
{
  "user_id": "jake",
  "blog_info": {
    "name": {
      "first": "iOS 앱 개발 알아가기",
      "second.example": "SwiftUI 앱 개발 알아가기"
    }
  }
}
  • var options: KeyOptions? 을 정의하여 사용

  • CodingKeys에다 var options: KeyOptions?을 정의
    • 아래 코드 의미는, `dot`키워드를 +로 쓴다는 의미이므로 이전에 dot으로 썼던 부분을 +로 사용하고, key값 내에서 dot 사용
    • blog_info+name+second까지는 +로 접근하고, 실제 값 second.exmaple은 그대로 dot으로 이용
      import KeyedCodable
      /*
      {
        "user_id": "jake",
        "blog_info": {
          "name": {
            "first": "iOS 앱 개발 알아가기",
            "second.example": "SwiftUI 앱 개발 알아가기"
          }
        }
      }
       */
      struct MyModel: Codable {
        enum CodingKeys: String, KeyedKey {
          case userID = "user_id"
          case blogName = "blog_info+name+second.example"
          
          var options: KeyOptions? {
            switch self {
            case .blogName:
              return KeyOptions(delimiter: .character("+"), flat: .none)
            default:
              return nil
            }
          }
        }
        var userID: String?
        var blogName: String?
      }

* 전체 코드: https://github.com/JK0369/ExKeyedCodable

* 참고

https://github.com/dgrzeszczak/KeyedCodable

Comments