iOS framework
[iOS - swift] json, codable, nestedContainer(keyedBy:forKey:), KeyedCodable로 중첩 모델 처리 방법
jake-kim
2022. 2. 9. 23:45
* 기본적인 Codable, Decode, Encode 개념은 이곳 참고
* 관련 포스팅 글
- json, codable, nestedContainer(keyedBy:forKey:), Decoding 중첩 모델 처리 방법 (디코딩)
- json, codable, nestedContainer(keyedBy:forKey:), Encoding 중첩 모델 처리 방법 (인코딩)
- json, codable, nestedContainer(keyedBy:forKey:), KeyedCodable로 중첩 모델 처리 방법
중첩 모델을 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를 쉽게 접근할 수 있는 기능

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
* 참고