Jiseob Kim

iOS Developer

Swift - Property Wrapper (feat. Codable 3ํŽธ)

11 Jul 2021 » Swift


์ง€๋‚œ ํŽธ์— ์ด์–ด์„œ ์ง„ํ–‰!


๋ชฉํ‘œ: ์ „ํŽธ ๋งˆ์ง€๋ง‰์— ์–ธ๊ธ‰ํ•œ๊ฒƒ ์ฒ˜๋Ÿผ Generic์„ ์ด์šฉํ•˜์—ฌ ๋ฐ˜๋ณต๋˜๋˜ ์ฝ”๋“œ๋ฅผ ์ค„์ด๊ธฐ


์ด ์‹œ๋ฆฌ์ฆˆ๋ฅผ ์ด์–ด๊ฐ€๋ฉด์„œ ํ•ด์˜จ๊ฒƒ๋“ค์„ ์ •๋ฆฌํ•˜๋ฉด ๋‹จ์ˆœํ•˜๊ฒŒ๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™์ด ๋ณผ ์ˆ˜ ์žˆ๋‹ค.

JSON ๊ฐ’์„ ๊ฐ์ฒดํ™” ํ• ๋•Œ ๋‹ค์Œ๊ณผ ๊ฐ™์€ ์œ ํ˜•๋“ค์ด ์žˆ์—ˆ๋‹ค.

  1. ์˜ต์…”๋„ Int -> Int (Default: 0)
  2. ์˜ต์…”๋„ String -> String (Default: โ€œโ€)
  3. ์˜ต์…”๋„ String -> Bool (Default: false)

(3์˜ ๊ฒฝ์šฐ๋Š” { "abc": "Y" } ๊ฐ™์€ ๊ฐ’์„ Bool ํ˜•ํƒœ๋กœ ๋ฐ”๊ฟ”์ฃผ๋Š” ๋‹ค๋ฅธ ํƒ€์ž… ์ฒ˜๋ฆฌ์˜€๋‹ค.)


์ด๊ฒƒ์„ ์ด์˜๊ฒŒ ๋ฌถ์–ด๋ณด๋ฉด ๋‹ค์Œ๊ณผ ๊ฐ™๋‹ค.

์˜ค๋ฅธ์ชฝ์˜ ๊ฒฝ์šฐ๋Š” ๊ฐ ์ฒ˜๋ฆฌ๋ฐฉ๋ฒ•์ด ๋‹ค๋ฅด๋ฏ€๋กœ Generic์œผ๋กœ ๋ฌถ๊ธฐ๊ฐ€ ํž˜๋“ค๊ฒ ์ง€๋งŒ

์™ผ์ชฝ์˜ ๊ฒฝ์šฐ๋Š” ๊ณตํ†ต์ ์œผ๋กœ โ€œํƒ€์ž…์ด ๊ฐ™๊ณ  ๊ธฐ๋ณธ๊ฐ’์„ ๊ฐ€์ง„๋‹ค.โ€ ๋ผ๋Š” ์ ์„ ์ด์šฉํ•˜์—ฌ ๋ฌถ์–ด๋ณด๋„๋ก ํ•˜์ž

๊ทธ๋ฆฌ๊ณ  ๋ฐฐ์—ด๋„ JSON ๊ฐ’์œผ๋กœ ์ž์ฃผ ๋‚˜์˜ค๋‹ˆ ์ด๋ ‡๊ฒŒ ๋ฌถ์–ด๋ณด์ž.

๊ทธ๋ฆฌ๊ณ  ์ตœ์ข…์ ์œผ๋กœ ๋ชจ๋“ ๊ฒƒ์€ JSON ๋ฐ์ดํ„ฐ๋ฅผ ์ฒ˜๋ฆฌ ํ•˜๊ธฐ ์œ„ํ•œ ๊ฒƒ์ด๋ฏ€๋กœ ๋ถ„์‚ฐ๋˜์–ด์žˆ์œผ๋ฉด ๊ด€๋ฆฌ๊ฐ€ ์–ด๋ ค์šฐ๋‹ˆ

์ „๋ถ€๋ฅผ ๋ฌถ์ž.


๊ธฐ๋ณธ๊ฐ’ ์ฒ˜๋ฆฌ๋Š” ์ด์ „ํŽธ์— ํ–ˆ์œผ๋‹ˆ ์ด๋ฒˆํŽธ์€ Generic์„ ์ค‘์ ์œผ๋กœ ๋ณด๊ฒŒ ๋ ๊ฒƒ์ด๊ณ ,

๋‹ค๋ฅธ ํƒ€์ž…์ฒ˜๋ฆฌ ๋‹ค์ŒํŽธ์—์„œ ์ง„ํ–‰ ์˜ˆ์ •(์ถ”๊ฐ€๋กœ decodable Array๋„!)


๋‹จ์ผ์˜ Property Wrapper๋ฅผ ์ด์šฉํ•˜์—ฌ ์‚ฌ์šฉ์€ ๋ถˆ๊ฐ€๋Šฅํ•˜๋‹ค.

์—ฌ๊ธฐ์„œ์˜ ๊ฒฝ์šฐ ์ดˆ๋ฐ˜์— ๋ฌถ์€ ๊ฒƒ์ฒ˜๋Ÿผ ๊ฐ™์€ ๊ธฐ๋ณธํ˜• ์ฒ˜๋ฆฌ๋žฉํผ, ๋‹ค๋ฅธ ํƒ€์ž…์šฉ ๋žฉํผ๋Š” ๋ณ„๋„๋กœ ์šด์šฉ๋˜๋ฉฐ

Enum์„ ์ด์šฉํ•˜์—ฌ ๋ฌถ์–ด์ฃผ๊ฒŒ ๋œ๋‹ค.


Property Wrapper & ๊ธฐ๋ณธ๊ฐ’ ์ฒ˜๋ฆฌ ๊ทธ๋ฆฌ๊ณ  Generic

Protocol

Generic์œผ๋กœ ๋ฌถ๊ธฐ์˜ ํฌ์ธํŠธ๋Š” ํ”„๋กœํ† ์ฝœ์„ ์ด์šฉํ•˜๋ฉฐ ์ดˆ๊ธฐ๊ฐ’์„ ๋ฐ›๋Š”๋‹ค๋Š” ์ ์ด๋‹ค.

 protocol DefaultWrapperAvailable {
    associatedtype ValueType: Decodable
    static var defaultValue: ValueType { get }
}


์œ„์˜ ์ฝ”๋“œ๋ฅผ ๋ณด์ž.

๊ธฐ๋ณธ๊ฐ’ ๋žฉํผ๋ฅผ ์ด์šฉํ•  ์ˆ˜ ์žˆ๋Š” ํ”„๋กœํ† ์ฝœ์„ ๋งŒ๋“ค์—ˆ๋‹ค.

associatedtype๋ผ๋Š” ๊ฒƒ์€ ํ”„๋กœํ† ์ฝœ์—์„œ ์“ฐ์ผ ์ˆ˜ ์žˆ๋Š”๋ฐ,

๋Š๋‚Œ์€ ์•ฝ๊ฐ„ Generic๊ณผ ๋น„์Šทํ•˜๋‹ค.

์ด ์ž๋ฆฌ์— ์–ด๋–ค ํƒ€์ž…์ด๋“  ์˜ฌ ์ˆ˜๊ฐ€ ์žˆ๋‹ค.


ํ”„๋กœํ† ์ฝœ์ด๋ผ๋Š” ์ž์ฒด๊ฐ€ ์„œ๋กœ์˜ ์˜์กด์„ฑ์„ ์ค„์—ฌ์ฃผ๊ณ  ์ ์šฉ์‹œ ํ•ด๋‹น ๊ทœ์•ฝ? ๊ทœ์น™?๋“ค์„ ๋”ฐ๋ผ์•ผ ํ•œ๋‹ค๋Š” ๊ฒƒ์ธ๋ฐ

๊ทธ๋Ÿฌ๋‹ค๋ณด๋‹ˆ ๋ฒ”์šฉ์„ฑ์ด ํ•„์š”ํ•ด์„œ ๋งŒ๋“ค์–ด์ง„๊ฒŒ ์•„๋‹Œ๊ฐ€ ์‹ถ๋‹ค. (๊ฐœ์ธ์  ์ƒ๊ฐ)


๋‘๋ฒˆ์งธ ์ค„์„ ๋ณด๊ณ  ๋‚˜์„œ ์ข€๋” ์„ค๋ช…์„ ํ•ด์•ผ ์ดํ•ดํ•˜๊ธฐ๊ฐ€ ์‰ฌ์šธ๋“ฏ ํ•˜๋‹ค.

static var๋กœ ์„ ์–ธ์„ ํ•ด์ค€ ํ”„๋กœํผํ‹ฐ์˜ ํƒ€์ž…์„ ๋ณด๋ฉด ValueType์ด๋‹ค.

์ €๋Ÿฐ ํƒ€์ž…์€ ์‹ค์ œ ์—†์ง€๋งŒ ๊ทธ ์œ—์ค„์— associatedtype๋กœ ์ ์–ด์ฃผ์—ˆ๊ณ , ๊ทธ ํƒ€์ž…์ด๋ผ๋Š” ์˜๋ฏธ๋‹ค.

๊ทธ๋ž˜์„œ ํ•ด๋‹น ํ”„๋กœํ† ์ฝœ์„ ์ ์šฉ์‹œํ‚ค๊ฒŒ ๋œ๋‹ค๋ฉด ๋ฐ˜๋“œ์‹œ ValueType์ด๋ž€ ์–ด๋–ค ํƒ€์ž…์ธ๊ฐ€๋ฅผ ๋ช…์‹œ๋ฅผ ํ•ด์ค˜์•ผํ•œ๋‹ค.


ํ•˜์ง€๋งŒ, ์กฐ๊ฑด์ด ํ•˜๋‚˜ ๋” ๋ถ™์–ด์žˆ๋‹ค. ์ด ํƒ€์ž…์€ Decodable์„ ๋”ฐ๋ผ์•ผ ํ•œ๋‹ค๋Š” ์ ์ด๋‹ค.


associatedtype๋Š” ์ด์ •๋„๋กœ๋งŒ ์„ค๋ช…์„ ํ•˜๊ณ ,

์™œ ํ–ˆ๋Š”์ง€๊ฐ€ ์ค‘์š”ํ•˜๋‹ค.


ํ†ต์‹ ํ›„ Decodable์„ ์ด์šฉํ•˜์—ฌ

๋‹ค์–‘ํ•œ ์ž๋ฃŒํ˜•์˜ ๊ธฐ๋ณธ๊ฐ’์„ ์ฒ˜๋ฆฌํ•˜๋Š” ๋ถ€๋ถ„์ด๊ธฐ ๋•Œ๋ฌธ์—

associatedtype์ด ํ•„์š”ํ–ˆ๊ณ , ์ด ์ž๋ฃŒํ˜•๋“ค์€ Decodable์„ ๋”ฐ๋ผ์•ผํ•œ๋‹ค.

๊ทธ๋ฆฌ๊ณ  ๊ธฐ๋ณธ๊ฐ’์€ ๋‹จ์ˆœํžˆ ํ•ด๋‹น ํ”„๋กœํ† ์ฝœ์„ ๋”ฐ๋ฅด๋Š” ๊ฐ์ฒด์—์„œ

๊ธฐ๋ณธ๊ฐ’์ด ๋ฌด์—‡์ธ์ง€๋งŒ ํ•„์š”ํ•˜๋ฏ€๋กœ ํƒ€์ž… ํ”„๋กœํผํ‹ฐ(static var)๋กœ์จ get๋งŒ ํ•„์š”ํ•˜๋‹ค.


์ดˆ๊ธฐํ™”๋Š” ๋ถˆํ•„์š”ํ•˜๋‹ค.

์ด ๊ธ€์—์„œ ์ดˆ๊ธฐํ™”๊ฐ€ ๋ถˆํ•„์š”ํ•˜๋‹ค๋Š” ๋ง์ด ์ž์ฃผ ๋‚˜์˜จ๋‹ค.


Property Wrapper ์„ ์–ธ

@propertyWrapper 
struct Wrapper<T: JSONDefaultWrapperAvailable> {
    typealias ValueType = T.ValueType
    var wrappedValue: ValueType
    init() {
        wrappedValue = T.defaultValue
    }
}

๋žฉํผ ์ƒ์„ฑ ๋ถ€๋ถ„์ด๋‹ค.

์ด์ „๊ณผ ๋‹ค๋ฅธ ๋ถ€๋ถ„์€ <T: JSONDefaultWrapperAvailable>์ด ๊ฐ€์žฅ ๋จผ์ € ๋ˆˆ์— ๋“ค์–ด์˜ค๊ฒŒ ๋ ํ…๋ฐ,

Generic T ๋ผ๋Š” ๊ฐ’์„ ์„ ์–ธ์‹œ ์ •์˜ ํ•ด์ค˜์•ผํ•˜๋ฉฐ, ์ด๋Š” ์œ„์—์„œ ๋งŒ๋“  ํ”„๋กœํ† ์ฝœ์„ ๋”ฐ๋ฅด๊ฒŒ ๋œ๋‹ค.

associatedtype ์˜€๋˜ ๋ถ€๋ถ„์„ typealias๋ฅผ ์ด์šฉํ•˜์—ฌ ์ •์˜๋ฅผ ํ•ด์ฃผ๊ณ , ๊ธฐ๋ณธ ๊ฐ’ ๋˜ํ•œ ์•Œ ์ˆ˜ ์žˆ๋‹ค.


์ฆ‰, ์šฐ๋ฆฌ๋Š” ๊ธฐ๋ณธ๊ฐ’์„ ๋ฏธ๋ฆฌ ๋ฐ›์•„์˜ฌ ์ˆ˜ ์žˆ๊ณ ,

๊ทธ ๋ฐ›์•„์˜จ ๊ฐ’์„ init ์„ ํ˜ธ์ถœํ• ๋•Œ ๋„ฃ๊ฒ ๋‹ค๋Š” ๊ฒƒ์ด๋‹ค.


์—ฌ๊ธฐ์„œ ํ•ต์‹ฌ์ด ์œ„ ๋ถ€๋ถ„์ด๋‹ค.


decode ํ•˜๊ฒŒ๋˜๋ฉด try๋ฅผ ํ•˜๊ฒŒ ๋ ํ…Œ๊ณ , JSON ์— ํ•ด๋‹น ํ‚ค๊ฐ€ ์—†์„ ๊ฒฝ์šฐ ์ด๋Š” ๋ฑ‰์–ด์ง€๊ฒŒ ๋˜๋Š”๋ฐ,

์ด๋•Œ init์„ ํ•˜๊ฒŒ ๋˜๊ณ , ๊ทธ ๋•Œ์˜ ๊ฐ’์ด ์ €defaultValue ๋ฅผ ์ด์šฉํ•˜์—ฌ init์„ ํ•ด์คŒ์œผ๋กœ์จ

ํ‚ค๊ฐ€ ์—†์–ด๋„ ๊ธฐ๋ณธ๊ฐ’์„ ์ •์˜ํ•  ์ˆ˜ ์žˆ๊ฒŒ ๋œ๋‹ค.


Protocol๊ณผ Enum

์œ„์— ๋งŒ๋“  ํ”„๋กœํ† ์ฝœ์„ ๋”ฐ๋ฅด๋Š” ๋ฌด์–ธ๊ฐ€๊ฐ€ ํ•„์š”ํ•˜๋‹ค.

๊ทธ๋ž˜์•ผ ๊ฐ ์ž๋ฃŒํ˜• ๋ณ„ ๊ธฐ๋ณธ๊ฐ’์„ ๋„ฃ์–ด์ฃผ๊ฒŒ ๋ ํ…Œ๋‹ˆ๊น!

๊ทธ๋ ‡๋‹ด ๋ณดํ†ต์€ ํ”„๋กœํ† ์ฝœ์„ ์ ์šฉ์‹œํ‚ฌ๋•Œ class ,struct ๋‘๊ฐ€์ง€์— ์ ์šฉ์„ ๋งŽ์ด ํ•˜๊ฒŒ ๋ ํ…๋ฐ,


๋‘๊ฐ€์ง€์˜ ๊ณตํ†ต์ ์ด ์žˆ๋‹ค.

ํ”„๋กœํผํ‹ฐ์™€ ๋ฉ”์†Œ๋“œ๊ฐ€ ์—†๋”๋ผ๋„ ์ดˆ๊ธฐํ™”๊ฐ€ ๋œ๋‹ค


๋‹ค์Œ์„ ๋ณด์ž

// 1. ์ƒ์„ฑ
class AA {
}
struct BB {        
}

// ์–ด๋”˜๊ฐ€..
// 2. ์„ ์–ธ ๋ฐ ์ดˆ๊ธฐํ™”
let aa = AA()
let bb = BB()

ํด๋ž˜์Šค AA์™€ ๊ตฌ์กฐ์ฒด BB๋ฅผ ๋งŒ๋“ค์–ด ์ฃผ์—ˆ๊ณ  ์ฃผ์„2์™€ ๊ฐ™์ด ์„ ์–ธ๊ณผ ์ดˆ๊ธฐํ™”๋ฅผ ํ•ด์ฃผ์—ˆ๋‹ค.

ํ”„๋กœํผํ‹ฐ์™€ ๋ฉ”์†Œ๋“œ๊ฐ€ ์—†์Œ์—๋„.


๋‹ค์‹œ ์šฐ๋ฆฌ๊ฐ€ ๋งŒ๋“  ํ”„๋กœํ† ์ฝœ์„ ๋ณด์ž.

protocol JSONDefaultWrapperAvailable {
    associatedtype ValueType: Decodable
    static var defaultValue: ValueType { get }
}

์—ฌ๊ธฐ์„œ ์ด ํ”„๋กœํ† ์ฝœ์„ ๋”ฐ๋ฅด๋Š” ๊ฒƒ์€ ์ดˆ๊ธฐํ™” ๋  ํ•„์š”์„ฑ์ด ์žˆ๋Š”๊ฐ€?


์•„๋‹ˆ๋‹ค ์ „ํ˜€ ํ•„์š”์—†๋‹ค.

๋‹จ์ˆœํ•˜๊ฒŒ

  1. ์ž๋ฃŒํ˜•์€ ๋ฌด์—‡์ธ๊ฐ€?
  2. ์ด ์ž๋ฃŒํ˜•์„ ๊ฐ€์ง„ ๊ธฐ๋ณธ๊ฐ’์€ ๋ฌด์—‡์ธ๊ฐ€?

์ด ๋‘๊ฐ€์ง€ ์ •์˜๋งŒ ํ•„์š”ํ• ๋ฟ ์ดˆ๊ธฐํ™”๋  ์ด์œ ๋Š” ์ „ํ˜€ ์—†๋‹ค.


๊ทธ๋ž˜์„œ Enum์ด ๋‚˜์˜ค๊ฒŒ ๋œ๋‹ค.

์—ด๊ฑฐํ˜•์—๋„ ํ”„๋กœํ† ์ฝœ์„ ์ ์šฉ์‹œํ‚ฌ ์ˆ˜ ์žˆ๋‹ค. ๊ทธ์น˜๋งŒ ๋‹ค๋ฅธ์ ์€ ์œ„์™€ ๊ฐ™์ด ์ดˆ๊ธฐํ™”๊ฐ€ ์•ˆ๋œ๋‹ค๋Š” ๊ฒƒ์ด๋‹ค.

์œ„์˜ ์Šค์ƒท์„ ๋ณด๋ฉด ๋‘๊ฐ€์ง€ ์—ด๊ฑฐํ˜•์ด ์žˆ๋‹ค.

  1. CC: ํ”„๋กœํผํ‹ฐ, ๋ฉ”์†Œ๋“œ ์ •์˜ x
  2. DD: ํ”„๋กœํผํ‹ฐ, ๋ฉ”์†Œ๋“œ ์ •์˜ x, ํ”„๋กœํ† ์ฝœ o

DD์˜ ๋‚ด์šฉ๋ฌผ์€ ์กฐ๊ธˆ ๋’ค์— ์–˜๊ธฐํ•˜๊ณ , ์ด๋ฏธ์ง€ ์•„๋ž˜ ๋ถ€๋ถ„ ์—๋Ÿฌ๋ฅผ ๋ณด๋ฉด

์ดˆ๊ธฐํ™”๋ฅผ ํ•  ์ˆ˜ ์—†๋‹ค๋Š”๊ฒƒ์ด ์ค‘์ ์ด๋‹ค. ๋ถˆํ•„์š”ํ•œ ์ดˆ๊ธฐํ™”๋Š” ์• ์ดˆ์— ์ฐจ๋‹จ!


๊ทธ๋ ‡๊ธฐ์— ์—ฌ๊ธฐ์„  class,struct๊ฐ€ ์•„๋‹Œ enum์— ํ”„๋กœํ† ์ฝœ์„ ์ ์šฉ ํ•  ๊ฒƒ์ด๋‹ค.



Enum ์ •์˜

์—ฌ๊ธฐ์„œ๋ถ€ํ„ฐ๋Š” ๊ฐ ์ž๋ฃŒํ˜•๋ณ„๋กœ Enum์— ํ”„๋กœํ† ์ฝœ์„ ์ ์šฉํ•˜์—ฌ ์ •์˜ํ•ด๋ณด์ž

์šฐ์„  Bool๊ณผ String๋ถ€ํ„ฐ!

enum True: JSONDefaultWrapperAvailable {
    // ๊ธฐ๋ณธ๊ฐ’ - true
    static var defaultValue: Bool { true }
}

enum False: JSONDefaultWrapperAvailable {
    // ๊ธฐ๋ณธ๊ฐ’ - false 
    static var defaultValue: Bool { false }
}

enum EmptyString: JSONDefaultWrapperAvailable {
    // ๊ธฐ๋ณธ๊ฐ’ - "" 
    static var defaultValue: String { "" }
}

์œ„์— ์—๋Ÿฌ ์ด๋ฏธ์ง€ ์บก์ณ๋ณธ๊ณผ ์ฐจ์ด๊ฐ€ ์žˆ๋‹ค๋ฉด(enum DD), typealias๊ฐ€ ์—†๋‹ค๋Š”์ ์ด๋‹ค.


์ถ”๋ก ์ด ๋œ ์ƒํ™ฉ์ธ๋ฐ, ํ”„๋กœํ† ์ฝœ ์ •์˜๋ถ€๋ฅผ ๋ณด๋ฉด defaultValue ํƒ€์ž…์ด ValueType ์ด์—ˆ๋Š”๋ฐ,

์ด๋ฅผ ์ด์šฉํ•˜์—ฌ defaultValue์— ํƒ€์ž…์„ ์ •์˜ํ•˜๋ฉด typealias๋Š” ๋ณ„๋„๋กœ ์ •์˜ํ•  ํ•„์š”๊ฐ€ ์—†๋‹ค.


์œ„์˜ ๋‚ด์šฉ์€ ๊ฐ„๋‹จํ•˜๋‹ค. ๊ธฐ๋ณธ๊ฐ’ ํ”„๋กœํ† ์ฝœ์„ ๋”ฐ๋ฅธ๋‹ค๋Š” ๊ฒƒ๊ณผ ๊ทธ๋ฆฌ๊ณ  ๊ธฐ๋ณธ๊ฐ’์„ ๊ฐ๊ฐ ์ง€์ •ํ•ด์คฌ๋‹ค๋Š” ์ ์ด๋‹ค.


JSON ๋ฐ์ดํ„ฐ ์ฒ˜๋ฆฌ ๋ถ€๋ถ„์˜ ๋ถ„์‚ฐ์„ ๋ง‰๊ธฐ์œ„ํ•ด property wrapper์™€ enum๋“ค์„ ๋‹ค์‹œ ํ•œ๋ฒˆ

enum์œผ๋กœ ๋ฌถ์ž

enum์œผ๋กœ ๋ฌถ๋Š” ์ด์œ ๋Š” ๋‹ค์‹œ ํ•œ๋ฒˆ ๋งํ•˜๋ฉด, ๋ถˆํ•„์š”ํ•œ ์ดˆ๊ธฐํ™”๋ฅผ ๋ฐฉ์ง€ ๊ฐ€๋Šฅํ•˜๊ธฐ ๋•Œ๋ฌธ์ด๋‹ค.


์ด๋ ‡๊ฒŒ.

enum JSONDefaultWrapper {
    // Property Wrapper
    @propertyWrapper
    struct Wrapper<T: JSONDefaultWrapperAvailable> {
        typealias ValueType = T.ValueType
        
        var wrappedValue: ValueType
        
        init() {
        wrappedValue = T.defaultValue
        }
    }
    
    // Type Enums
    enum True: JSONDefaultWrapperAvailable {
        // ๊ธฐ๋ณธ๊ฐ’ - true
        static var defaultValue: Bool { true }
    }

    enum False: JSONDefaultWrapperAvailable {
        // ๊ธฐ๋ณธ๊ฐ’ - false 
        static var defaultValue: Bool { false }
    }

    enum EmptyString: JSONDefaultWrapperAvailable {
        // ๊ธฐ๋ณธ๊ฐ’ - "" 
        static var defaultValue: String { "" }
    }
}

๊ทธ๋ฆฌ๊ณ  ์ด์ „ํŽธ์— ๋‚˜์˜จ ๊ฒƒ๋“ค์„ ๋˜์ƒˆ๊ฒจ๋ณด๋ฉด,

  1. ๋žฉํผ์— init(from decoder: Decoder) ์ •์˜๊ฐ€ ํ•„์š”ํ•˜๋‹ค
extension JSONDefaultWrapper.Wrapper: Decodable {
    init(from decoder: Decoder) throws {
        let container = try decoder.singleValueContainer()
        self.wrappedValue = try container.decode(ValueType.self)
    }
}
  1. KeyedDecodingContainer์— extensionํ•˜์—ฌ decode ๋ฉ”์†Œ๋“œ ์ถ”๊ฐ€ ํ•„์š”ํ•˜๋‹ค.
extension KeyedDecodingContainer {
    func decode<T: JSONDefaultWrapperAvailable>(_ type: JSONDefaultWrapper.Wrapper<T>.Type, forKey key: Key) throws -> JSONDefaultWrapper.Wrapper<T> {
        try decodeIfPresent(type, forKey: key) ?? .init()
    }
}


์œ„์™€ ๊ฐ™์ด ์…‹ํŒ…์„ ํ•ด์ฃผ๋ฉด ์‚ฌ์šฉํ•  ์ค€๋น„๋Š” ๋๋‚ฌ๋‹ค.

์‹ค์ œ ์ ์šฉ ์ฝ”๋“œ์™€ ์ถœ๋ ฅ ๊ฐ’์ด๋‹ค.


์œ„ ์ด๋ฏธ์ง€๋ฅผ ์„ค๋ช…ํ•˜๋ฉด ๋‹ค์Œ๊ณผ ๊ฐ™๋‹ค.

  1. ๋งจ ์ฒซ์ค„์— stringValue๋Š” ์˜ต์…”๋„์ด ์•„๋‹Œ String์ด๋‹ค.
  2. JSON ํ˜•ํƒœ์˜ ๋ฐ์ดํ„ฐ๋Š” stringValue์™€ ๊ด€๋ จ๋œ ํ‚ค๊ฐ€ ์—†๋‹ค. (์ด๋Ÿฌํ•œ ์ด์œ  ๋•Œ๋ฌธ์— ๋ณ„๋„ ์ฒ˜๋ฆฌ๊ฐ€ ์—†์—ˆ๋‹ค๋ฉด error๋ฅผ ๋ฑ‰๊ฒŒ ๋œ๋‹ค.)
  3. Decode๋ฅผ ํ–ˆ์„๋•Œ, stringValue๊ฐ€ ๋นˆ๊ฐ’์ธ์ง€ ํ™•์ธ์ด ๋˜์—ˆ๋‹ค.


2๊ฐ€ ํ•ต์‹ฌ์ด๋‹ค. ๊ด€๋ จ ํ‚ค๊ฐ€ ์—†์ง€๋งŒ ๊ฐ์ฒด๊ฐ€ ์ž˜ ์ƒ์„ฑ ๋˜์—ˆ๋‹ค.


๊ทธ๋Ÿผ ์œ„์— enum์„ ์ „๋ถ€ ์ ์šฉํ•˜์—ฌ ๋‹ค์‹œ ๊ฒฐ๊ณผ๋ฅผ ๋ณด์ž.

์•„์ฃผ ์•„์ฃผ ์ž˜ ๋˜์—ˆ๋‹ค!

์ด์ œ JSON ๋ฐ์ดํ„ฐ์— ํ•ด๋‹น ํ‚ค๊ฐ€ ์—†์–ด๋„ ๋ฌธ์ œ ์—†๋‹ค!



๊ทธ์น˜๋งŒ ๋ชป์ƒ๊ธด๊ฑด ๋ฌธ์ œ๊ฐ€ ์žˆ๋‹ค.


@JSONDefaultWrapper.Wrapper<JSONDefaultWrapper.EmptyString> var stringValue: String
@JSONDefaultWrapper.Wrapper<JSONDefaultWrapper.True> var trueValue: Bool
@JSONDefaultWrapper.Wrapper<JSONDefaultWrapper.False> var falseValue: Bool

์•„ ์ด ์–ผ๋งˆ๋‚˜ ํ•ด๊ดดํ•˜๊ฒŒ ์ƒ๊ฒผ๋Š”๊ฐ€;;

๊ต‰์žฅํžˆ ๋”์ฐํ•˜๊ณ  ๊ฐ€๋…์„ฑ๋„ ๋ณ„๋กœ ๊ทธ๋ƒฅ ๋ณ„๋กœ๋‹ค.


typealias๋ฅผ ํ†ตํ•ด ์ •๋ฆฌ์ข€ ํ•ด์ฃผ์ž.


์ •๋ฆฌํ•˜๊ธฐ ์•ž์„œ ์›๋ž˜ enum๋“ค์€ JSONDefaultWrapper๋ฅผ ์น˜๊ณ 

.์„ ๋ˆ„๋ฅด๋ฉด ์ž๋™์™„์„ฑ์ด ์ดค๋ผ๋ฝ ๋œจ๊ฒŒ ๋ ํ…๋ฐ,

์ด ๋ถ€๋ถ„์„ ์ •๋ฆฌ ํ•  ๊ฒƒ์ด๋‹ˆ ์ž๋™์™„์„ฑ์— ํ‘œ์ถœ๋˜๊ธธ ์›์น˜ ์•Š๋Š”๋‹ค.

์ด๊ฒƒ๋“ค ๋˜ํ•œ ๋‹ค์‹œ ํ•œ๋ฒˆ enum์œผ๋กœ ๋ฌถ์–ด์ฃผ์ž

enum TypeCase {
    // Type Enums
    enum True: JSONDefaultWrapperAvailable {
        // ๊ธฐ๋ณธ๊ฐ’ - true
        static var defaultValue: Bool { true }
    }

    enum False: JSONDefaultWrapperAvailable {
        // ๊ธฐ๋ณธ๊ฐ’ - false
        static var defaultValue: Bool { false }
    }

    enum EmptyString: JSONDefaultWrapperAvailable {
        // ๊ธฐ๋ณธ๊ฐ’ - ""
        static var defaultValue: String { "" }
    }
}

์ด๋ ‡๊ฒŒ ๋ง์ด๋‹ค.


๊ทธ๋ฆฌ๊ณ  ๋‚˜์„  ์•„๊นŒ ๋ชป์ƒ๊ธด ๋ถ€๋ถ„๋“ค์„ typealias๋กœ ๋‹ค์‹œ ์ ์–ด์ฃผ์ž

enum JSONDefaultWrapper {
    typealias EmptyString = Wrapper<JSONDefaultWrapper.TypeCase.EmptyString>
    typealias True = Wrapper<JSONDefaultWrapper.TypeCase.True>
    typealias False = Wrapper<JSONDefaultWrapper.TypeCase.False>
    
    // ...
}

๊ทธ๋Ÿผ ๋‹ค์‹œ ์‚ฌ์šฉํ•˜๋Š” ๋ถ€๋ถ„์„ ๋ณด๋ฉด ๋‹ค์Œ๊ณผ ๊ฐ™์ด ์ •๋ฆฌ๊ฐ€ ๋œ๋‹ค.


@JSONDefaultWrapper.EmptyString var stringValue: String
@JSONDefaultWrapper.True var trueValue: Bool
@JSONDefaultWrapper.False var falseValue: Bool


์˜ค ํ›จ์”ฌ ๊น”๋”ํ•˜๋‹ค!

๊ทธ๋Ÿผ ์ด์ œ ์‚ฌ์šฉ๋ฒ•์„ ์•Œ์•˜์œผ๋‹ˆ ์ˆซ์ž๋“ค์— ๋Œ€ํ•ด์„œ๋„ ํ•ด๋ณด์ž.



์ˆซ์ž ์ฒ˜๋ฆฌ

์ผ๋ถ€๋กœ ์ˆซ์ž ์ž๋ฃŒํ˜•๋“ค์„ ๋”ฐ๋กœ ๋บ๋Š”๋ฐ

Int, Double, Float, CGFloat ๋“ฑ๋“ฑ ์ˆซ์ž ๊ด€๋ จ ์ž๋ฃŒํ˜•์ด ๋งŽ๋‹ค.

๊ทธ๋ ‡๋‹จ ๋ง์€ enum TypeCase ์•ˆ์— ๊ฐ ์ž๋ฃŒํ˜•๋ณ„ enum์ด ์ถ”๊ฐ€ ๋˜์–ด์•ผ ํ•œ๋‹ค๋Š” ๊ฒƒ์ธ๋ฐ

์ด๊ฒƒ์€ Generic์œผ๋กœ ๋นผ๋ฒ„๋ฆฌ์ž

์™„์„ฑ๋ณธ์€ ๋‹ค์Œ๊ณผ ๊ฐ™๋‹ค

enum TypeCase {
    // Type Enums
    enum True: JSONDefaultWrapperAvailable {
        // ๊ธฐ๋ณธ๊ฐ’ - true
        static var defaultValue: Bool { true }
    }

    enum False: JSONDefaultWrapperAvailable {
        // ๊ธฐ๋ณธ๊ฐ’ - false
        static var defaultValue: Bool { false }
    }

    enum EmptyString: JSONDefaultWrapperAvailable {
        // ๊ธฐ๋ณธ๊ฐ’ - ""
        static var defaultValue: String { "" }
    }
    
    enum Zero<T: Decodable>: JSONDefaultWrapperAvailable where T: Numeric {
        // ๊ธฐ๋ณธ๊ฐ’ - 0
        static var defaultValue: T { 0 }
    }
}

์ œ์ผ ์•„๋ž˜์— Zero๋ผ๊ณ  ์ถ”๊ฐ€ํ•ด์คฌ๋Š”๋ฐ Generic T๊ฐ€ decodeble์„ ๋”ฐ๋ฅด๋ฉฐ, Numeric๋˜ํ•œ ๋”ฐ๋ฅด๊ฒŒ ๋œ๋‹ค.

์—ฌ๊ธฐ์„œ ํฌ์ธํŠธ๋Š” Numeric ์ด๋ผ๋Š” ์ !

์ˆซ์ž ๊ด€๋ จ ์ฒ˜๋ฆฌ๋ฅผ ๋‹ค ํ•ด๋ฒ„๋ฆด ์ˆ˜ ์žˆ๋‹ค.

ํ•˜์ง€๋งŒ typealias๋Š” ์ •์˜ ํ•ด์ค˜์•ผ์ง€ ใ…  ์ฐจ๋งˆ ์ด๊ฑฐ๊นŒ์ง„ ๋ชป ์ค„์˜€๋‹ค.

typealias IntZero = Wrapper<JSONDefaultWrapper.TypeCase.Zero<Int>>
typealias DoubleZero = Wrapper<JSONDefaultWrapper.TypeCase.Zero<Double>>
typealias FloatZero = Wrapper<JSONDefaultWrapper.TypeCase.Zero<Float>>
typealias CGFloatZero = Wrapper<JSONDefaultWrapper.TypeCase.Zero<CGFloat>>


์ž ๊ทธ๋Ÿผ ๋‹ค์‹œ ์‹คํ–‰ํ•˜์—ฌ ์ถœ๋ ฅ๊ฐ’์„ ๋ณด์ž

ํฌํ‚„.. ๋๋‹ค..

์“ฐ๋ฉด์„œ๋„ ๋„ˆ๋ฌด๋‚˜ ํ—ท๊ฐˆ๋ ธ๋˜ ์ด๋ฒˆ ํฌ์ŠคํŒ…

๋„๋Œ€์ฒด ๋ช‡๋ฒˆ์„ ์ผ๋‹ค ์ง€์› ๋‹ค ํ•œ์ง€ ๋ชจ๋ฅด๊ฒ ๋„ค?


๋“œ๋””์–ด ๋‹ค์Œ ํฌ์ŠคํŒ…์ด ์ด ์‹œ๋ฆฌ์ฆˆ์˜ ๋งˆ์ง€๋ง‰์ผ๋“ฏ ํ•˜๋‹ค.


์ด ๊ธ€์—์„œ ์ •์˜๋œ JSONDefaultWrapper์˜ ์ „์ฒด ์ฝ”๋“œ๋ฅผ ๋ณด๊ณ  ๋งˆ๋ฌด๋ฆฌํ•˜๊ธฐ.

protocol JSONDefaultWrapperAvailable {
    associatedtype ValueType: Decodable
    static var defaultValue: ValueType { get }
}

enum JSONDefaultWrapper {
    typealias EmptyString = Wrapper<JSONDefaultWrapper.TypeCase.EmptyString>
    typealias True = Wrapper<JSONDefaultWrapper.TypeCase.True>
    typealias False = Wrapper<JSONDefaultWrapper.TypeCase.False>
    typealias IntZero = Wrapper<JSONDefaultWrapper.TypeCase.Zero<Int>>
    typealias DoubleZero = Wrapper<JSONDefaultWrapper.TypeCase.Zero<Double>>
    typealias FloatZero = Wrapper<JSONDefaultWrapper.TypeCase.Zero<Float>>
    typealias CGFloatZero = Wrapper<JSONDefaultWrapper.TypeCase.Zero<CGFloat>>
    
    // Property Wrapper
    @propertyWrapper
    struct Wrapper<T: JSONDefaultWrapperAvailable> {
        typealias ValueType = T.ValueType

        var wrappedValue: ValueType

        init() {
        wrappedValue = T.defaultValue
        }
    }

    enum TypeCase {
        // Type Enums
        enum True: JSONDefaultWrapperAvailable {
            // ๊ธฐ๋ณธ๊ฐ’ - true
            static var defaultValue: Bool { true }
        }

        enum False: JSONDefaultWrapperAvailable {
            // ๊ธฐ๋ณธ๊ฐ’ - false
            static var defaultValue: Bool { false }
        }

        enum EmptyString: JSONDefaultWrapperAvailable {
            // ๊ธฐ๋ณธ๊ฐ’ - ""
            static var defaultValue: String { "" }
        }
        
        enum Zero<T: Decodable>: JSONDefaultWrapperAvailable where T: Numeric {
            // ๊ธฐ๋ณธ๊ฐ’ - 0
            static var defaultValue: T { 0 }
        }
    }
}

extension JSONDefaultWrapper.Wrapper: Decodable {
    init(from decoder: Decoder) throws {
        let container = try decoder.singleValueContainer()
        self.wrappedValue = try container.decode(ValueType.self)
    }
}

extension KeyedDecodingContainer {
    func decode<T: JSONDefaultWrapperAvailable>(_ type: JSONDefaultWrapper.Wrapper<T>.Type, forKey key: Key) throws -> JSONDefaultWrapper.Wrapper<T> {
        try decodeIfPresent(type, forKey: key) ?? .init()
    }
}


๋.