์ง๋ ํธ์ ์ด์ด์ ์งํ!
์ง๋ํธ ๋ง์ง๋ง์ ๋จ์ ์ ์ ์ด์ฃผ๋ฉฐ ๋ง๋ฌด๋ฆฌ๋ฅผ ์ง์์๋ค.
์ด๋ฒํธ์ ๊ทธ ๋จ์ ์ ๋ณด์ํ๊ธฐ ์ํ ํธ.
๋ฐฐ์ฐ๋ ๋์ด๋๊ฐ ์๊ฐ๋ณด๋ค ๋์๋ค.
์ง๋ํธ์ ์ ์ ๋จ์ ์ ์ผ๋ถ๋ถ์ด๋ค
โฆ ํ์ง๋ง,
isHidden์ด๋ผ๋ ๊ฐ์ด ์์ ๊ฒฝ์ฐ, ๋ชจ๋ธ์ ํด๋น ๊ฐ์ ์ต์ ๋์ฒ๋ฆฌ ํด์ฃผ๋๋ผ๋ ์ด ์ฝ๋๋throwํ๊ฒ ๋๋ค. โฆ ์ค์ ๋ก ์๋ฒ์์๋Request์ ๋ฐ๋ผResponse๊ฐ ๋ฌ๋ผ์ง ๊ฐ๋ฅ์ฑ์ ์กด์ฌํ๋ค. ๊ทธ๋ ๊ธฐ์ ์ด ๋จ์ ์ ์น๋ช ์ ์ด๋ผ ์๊ฐ๋๋ค.
์ฐจ๊ทผ ์ฐจ๊ทผ ํ๋์ฉ ํ์ด๋๊ฐ๋ณด์
์ฐ์ ์ String์ ๋ํด์๋ง ๋ณด๊ณ Generic์ ์ด์ฉํด ๋ฒ์๋ฅผ ๋ํ ๋ณด์.
๊ทธ๋ฆฌ๊ณ ์ด์ ๊ณผ ๊ฐ์ด ๋ค๋ฅธ ํ์ ์ผ๋ก ์บ์คํ ํ๋ ๋ฐฉ๋ฒ๋ ์์๋ณผ ์์ ์ด๋ค.
Codable ํํธ๋ง 3ํธ์ผ๋ก ๋๋ ์ผ ํ ๋ฏ ํ๋ค.
์ด๊ฒ๊น์ง ํ๊ณ ๋์์ผ ๋น๋ก์ ์ค๋ฌด์ ์จ๋ ๋๊ฒ ๋ค๋ ํ์ ์ด ๋ค์๋ค.
์ฒซ๋ฒ์งธ JSON ํํ๋ ๋ค์๊ณผ ๊ฐ๋ค
{
"usrNm" : "KJS"
}
๊ทธ๋ฆฌ๊ณ Class ํํ๋ ๋ค์๊ณผ ๊ฐ๋ค
class UserClass: Decodable {
var usrNm: String
var usrAddress: String
}
์ฌ๊ธฐ์ ์ฃผ๋ชฉํด์ผํ ์ ์ ํด๋์ค์๋ usrAddress๋ ์กด์ฌํ์ง๋ง JSON์์ ์กด์ฌํ์ง ์๋๋ค๋์ ์ด๋ค.
๋ค์ ๋งํด, JSON์ ์์ ๊ฒฝ์ฐ ๊ธฐ๋ณธ๊ฐ ์ฒ๋ฆฌ๋ฅผ ํ๊ณ ์ถ๋ค๋ ๊ฒ์ด๋ค.
Wrapper ์์ฑ
@propertyWrapper
struct JsonStringWrapper: Decodable {
let wrappedValue: String
}
๋ฉํผ๊ฐ Decodable ํ๋กํ ์ฝ์ ๋ฐ๊ณ ์๋ค. ๊ทธ๋ฆฌ๊ณ init decode ๋ ๋, ์ ์๊ฐ ํ์ํ๋ค.
extension JsonStringWrapper {
init(from decoder: Decoder) throws {
let container = try decoder.singleValueContainer()
self.wrappedValue = try container.decode(String.self)
}
}
์ฌ๊ธฐ๊น์ง ์ด์ ํธ๊น์ง ๊ฐ๋ค.
๋ฌธ์ ๋ ์๋ ๊ธฐ๋ณธ ๊ฐ์ ๋ํด ์ฒ๋ฆฌ๊ฐ ํ์ํ๋ค.
์ฐ์ ์์ ๋ฉํผ์ init()์ ์ถ๊ฐํด์ฃผ์
@propertyWrapper
struct JsonStringWrapper: Decodable {
let wrappedValue: String
init() {
wrappedValue = ""
}
}
๊ทธ๋ผ ๋จ์ ์ด๊ธฐํ์ ๋น๊ฐ์ ๊ฐ์ง๊ฒ ๋๋ค.
๊ทธ๋ฆฌ๊ณ ๋ค์์ด ํต์ฌ์ด๋ค.
extension KeyedDecodingContainer {
func decode(_ type: JsonStringWrapper.Type, forKey key: Key) throws -> JsonStringWrapper {
try decodeIfPresent(type, forKey: key) ?? .init()
}
}
key๊ฐ ์กด์ฌํ๋ฉด extension JsonStringWrapper ๋ถ๋ถ์ decode๋ฅผ ํ๊ฒ๋๊ณ
ํด๋น ํค๊ฐ ์๋ค๋ฉด decodeIfPresent๋ nil์ ๋ฑ๊ธฐ ๋๋ฌธ์ .init() ์ด ๋๋ค.
์ด๊ฑธ ๋จผ์ ํ์ด์ผํ๋๋ฐ, ์ ํธ ๋์ด๋๊ฐ ๋ ๋์๋๊ฑฐ ๊ฐ๋ค? ์๋๊ฐ ๋ชจ๋ฅด๊ฒ ๋ค. ์ extension KeyedDecodingContainer์ ๋๋ ๋๋ฌด ์์ํด์ ์ด๊ฒ ๋ ์ด๋ ค์ด๊ฑฐ ๊ฐ๊ธฐ๋ํ๊ณ โฆ
์์ ๋ง์ ๊ฑฐ์ ๊ฐ์ง๋ง ์กฐ๊ธ ๋ค๋ฅด๊ฒ ํด๋ณด์๋ฉด
key๋ ์ ์์ JSON ๊ฐ ์์ usrNm์ด๋ผ๋ ๊ฒ์ ์๋ฏธํ๊ณ ,
์ด๊ฒ ์กด์ฌํ๋ฉด ๊ธฐ์กด init(from decoder:)๋ฅผ ํ์์ ์ด๊ธฐํ๋ฅผ ์ํค๋ ๊ฒ์ด๊ณ
์กด์ฌํ์ง ์์ผ๋ฉด ๋ฏธ๋ฆฌ ์ ์ํ init()์ ํ๊ฒ ๋๋ ๊ฒ์ด๋ค.
๊ฑฐ์ ๊ฐ์ ๋ง์ด์ง๋ง ๋ฐ๋ณต์ ์ค์ํ๋๊น..!
๊ฒฐ๋ก ์ ์ ๋ ๊ฒํ๋ฉด ์๋ฒ์์ JSON์ ๊ฐ์ด ์๋ด๋ ค์๋ Decodable์ ์ด์ฉํด ๋ฏธ๋ฆฌ ์ ํ ์ด๊ธฐ๊ฐ์ผ๋ก ๊ฐ์ฒด๋ฅผ ์์ฑํ ์ ์๋ค๋๊ฒ!
์ ์ด๊ฒ์ ํ๋์ง๊ฐ ์ค์ํ๋ค.
์ด๊ฒ์ ์์ํ ํ์คํ ๋ฆฌ๋ฅผ ์ ๋ฆฌํด๋ณด์๋ฉด ์๋์ ๊ฐ๋ค.
1.Codable์ ์ด์ฉํ์ฌ
์๋ฒ์์ JSON ๊ฒฐ๊ณผ ๊ฐ์ ๊ฐ์ฒดํ ํ ์ ์๋ค
์์ฌ์ด์
JSON ๊ฒฐ๊ณผ์ ์ํ๋ ๊ฐ์ด ์์๋, 0๊ณผ ๊ฐ์ ์ด๊ธฐ๊ฐ์ ์ฃผ๊ณ ์ถ๋ค๊ฑฐ๋ ์๋ฃํ์ ๋ณ๊ฒฝํ๊ณ ์ถ๋ค๋ฉด
init(from decoder: Decoder) ๋ถ๋ถ์ ๋ชจ๋ ํ๋กํผํฐ์ ๋ํด ๋ค ์ค์ ์ ํด์ค์ผ ํ๋ค.
init(from decoder: Decoder) throws {
let container = try decoder.singleValueContainer()
// ๋ชจ๋ ํ๋กํผํฐ ์ ์ธํด์ฃผ๊ธฐ
self.propertyA = try container.decode(String.self)
self.propertyB = try container.decode(Bool.self)
self.propertyC = try container.decode(Int.self)
self.propertyD = try container.decode(String.self)
...
}
๋ฐ๋ผ๋์
ํ๋กํผํฐ ๊ฐฏ์ ๋งํผ initํํธ์ ์ญ ๋์ดํ๋ ๊ฒ์ ๊ฐ์ ์ ํ์์ฑ์ ๋๋
2. Property Wrapper๋ฅผ ์ด์ฉํ๋ฉด
init(from decoder: Decoder)๋ฅผ ์ํด๋ ๋๋ค.
์ฅ์
@propertyWrapper
struct StringBoolConverter {
let wrappedValue: Bool
}
extension StringBoolConverter: Codable {
init(from decoder: Decoder) throws {
let container = try decoder.singleValueContainer()
let stringBool = try? container.decode(String.self)
wrappedValue = stringBool == "Y"
}
}
์์ ๊ฐ์ ๋ฉํผ๋ฅผ ๋ง๋ค์ด ๋๋ค๋ฉด
{
"isHidden" : "Y"
}
์์ ๊ฐ์ JSON ๊ฒฐ๊ณผ๋
struct Model: Codable {
@StringBoolConverter var isHidden: Bool
}
์์ ๊ฐ์ด ํด์ฃผ๋ฉด ๊น๋ํ๊ฒ init(from decoder:)ํํธ ์์ด ์ฒ๋ฆฌ๊ฐ ๊ฐ๋ฅํ๋ค.
์์ฌ์ด์
JSON ๊ฒฐ๊ณผ ๋ด์ isHidden์ด๋ผ๋ ๊ฐ์ด ์๋ค๋ฉด init์ throw๋ฅผ ํ๊ฒ ๋์ด ๊ฐ์ฒด ์์ฑ์ด ๋์ง ์๋๋ค.
๋ฐ๋ผ๋์
ํ๋กํผํฐ๋ ๋ง๋ค์ด๋์ง๋ง JSON๊ฒฐ๊ณผ ๋ด์ ํด๋น ํค๊ฐ ๋ด๋ ค์ค์ง ์์ ๊ฒฝ์ฐ ๋ฏธ๋ฆฌ ์ง์ ํ default๊ฐ์ผ๋ก ์ด๊ธฐํ ๋๊ธธ ๋ฐ๋.
3. 2์ Property Wrapper ๊ฐ์
์์ ์ธ๋ฒ์ด๋ ๋งํ์ง๋ง ํ๋ฒ๋ ๋งํ์๋ฉด key๊ฐ ์กด์ฌํ๋ฉด ๋ฏธ๋ฆฌ ์ ์ํ init์ ํ์ฐ๊ฒ ํ๊ณ
ํค๊ฐ ์๋ค๋ฉด ๋ฏธ๋ฆฌ ์ ํ ์ด๊ธฐ๊ฐ์ผ๋ก ์ด๊ธฐํ๋๊ฒ ํ์
@propertyWrapper
struct JsonStringWrapper: Decodable {
let wrappedValue: String
init() {
wrappedValue = ""
}
}
extension KeyedDecodingContainer {
func decode(_ type: JsonStringWrapper.Type, forKey key: Key) throws -> JsonStringWrapper {
try decodeIfPresent(type, forKey: key) ?? .init()
}
}
์ฅ์
JSON์ ์ ์ํ ํค๊ฐ ์์ด๋ init()์ ์ ์ํ๋๋ก ๋ฉํผ๊ฐ์ ""์ผ๋ก ๋ค์ด๊ฐ๊ฒ ๋๋ค.
์์ฌ์ด์
๋ง์ง๋ง ์ฝ๋๋ง ๋ณด์
extension KeyedDecodingContainer {
func decode(_ type: JsonStringWrapper.Type, forKey key: Key) throws -> JsonStringWrapper {
try decodeIfPresent(type, forKey: key) ?? .init()
}
}
์ด๋ถ๋ถ์ type์ ์ด๋ฒ์ ๋ง๋ JsonStringWrapper์ ๋ํด์๋ง ๋ฐ์์ ํ๋ ๋ฉ์๋์ด๋ค.
๋ค์ ๋งํด,
JSON ๊ฒฐ๊ณผ ๊ฐ๋ค ์ฒ๋ฆฌ๋ฅผ ์ํด ๋ง์ฐฌ๊ฐ์ง๋ก Int, Double, Float, Bool ๋ฑ์ ๋ฉํผ๋ก ๋ง๋ค๊ฒ ๋๋ค๋ฉด
ํด๋น ๋ฉ์๋๋ ๋์ผํ๊ฒ ์ฆ๊ฐ๋๊ฒ ๋๋ค.
๊ทธ๋ผ์ ๋ฐ๋ผ ์ด๊ฒ ๋ํ ์์ฝ๋ค๊ณ ๋๊ปด์ง๋ฉฐ ๊ฐ์ ์ ํ์์ฑ์ ๋๋ผ๊ฒ ๋์๋ค.
๊ทธ๋์ ์ด ์์ฌ์์ ๋ ๋ฌ๋๊ธฐ ์ํด ๋ค์ํธ์ Generic์ ์ด์ฉํ์ฌ
ํด๋น ๋ถ๋ถ์ ์ค์ด๋ ๊ฒ์ ์ด์ ์ ๋ง์ถฐ ๊ธ์ ์จ๋ณด๊ณ ์ ํ๋ค.
๋.