개발/iOS
Decoder 3가지 메소드 정리 (Container 종류)
빙수킹
2023. 1. 14. 17:53
Decoder의 메소드들 (Container 3가지)
- func container
(keyedBy: Key.Type) throws -> KeyedDecodingContainer - func singleValueContainer() throws -> SingleValueDecodingContainer
- func unkeyedContainer() throws -> UnkeyedDecodingContainer
1. KeyedDecodingContainer
- CodingKey로 넘긴 키타입에 맞는 키를 가지는 값들을 포함하는 컨테이너.
- 일반적인 key: value 형태의 json을 디코딩할 때 사용.
- 우리가 일반적으로 property를 몇개 가진 struct에 Decodable을 준수시키면 init에서 이 형태의 Container가 사용된다.
예시
https://api.sampleapis.com/coffee/hot
위 API 아웃풋인 json을 디코딩한다고 가정하자.
struct Coffee: Decodable {
let title: String?
let description: String
let ingredients: [String]
let image: String
let id: Int
enum CodingKeys: CodingKey {
case title
case description
case ingredients
case image
case id
}
init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
self.title = try container.decodeIfPresent(String.self, forKey: .title)
self.description = try container.decode(String.self, forKey: .description)
self.ingredients = try container.decode([String].self, forKey: .ingredients)
self.image = try container.decode(String.self, forKey: .image)
self.id = try container.decode(Int.self, forKey: .id)
}
}
2. SingleValueDecodingContainer
- a single primitive value 을 가지는 Container.
- key, value 없이, 값 1개(통으로) 때만 사용하는 아이
- 값 1개라는건 예를들어 String 1개도 되겠지만, 1개의 array도 된다.
예시1
가장 상위의 껍질 struct가 불필요할 때 통 container를 만들어서 내부를 decode하면 좋을 것 같다.
ex) 1개의 array가 들어있는 struct
struct ResultList: Decodable {
let results: [SomeResult]
init(from decoder: Decoder) throws {
results = try decoder.singleValueContainer().decode([SomeResult].self)
}
}
struct SomeResult: Decodable {
}
예시2
각 최종 primitive type 에서 케이스를 나눠서 self에 넣어주고 싶을 때
ex) enum의 init에서 singleValueContainer 사용
internal enum Animal: String, Decodable {
case dog = "DOG"
case cat = "CAT"
case unknown
internal init(from decoder: Decoder) throws {
if let rawValue = try? decoder.singleValueContainer().decode(RawValue.self),
let animal = Animal(rawValue: rawValue) {
self = animal
} else {
self = .unknown
}
}
}
3. UnkeyedDecodingContainer
- key가 없는 값들을 보유하기에 적합한 Container
- 순서대로 decode 된다.
예시
아래와 같은 key가 없는 array 형태의 json 파일을 디코딩 할 때 적합
["a", "b", 1, 3, 0.3]
특이사항: decode 함수가 mutable 이다!!
struct ArrayDecoder: Decodable {
let a: String
let b: String
let c: Int?
let d: Bool
let e: Double
enum CodingKeys: CodingKey {
case a
case b
case c
case d
case e
}
init(from decoder: Decoder) throws {
// decode 함수를 부르면 var로 바꾸라고 한다.
// 다른 아이들과 다르게 decode 함수가 mutating이다.
// 그 이유는 decode하면 그 아이가 array에서 pop(?)되기 때문
var container = try decoder.unkeyedContainer()
self.a = try container.decode(String.self)
self.b = try container.decode(String.self)
// decodeIfPresent 에서 실패해서 nil이 나오면 pop되지 않는다.
self.c = try container.decodeIfPresent(Int.self)
// 값이 nil이면 true, 아니면 false
self.d = try container.decodeNil()
self.e = try container.decode(Double.self)
}
}