Published on

๐ŸŽ Swift - Property Wrapper

Authors
  • avatar
    Name
    ์ด์ฐฝ์ค€
    Twitter

์˜ค๋Š˜์€ UserDefaults๋ฅผ ์–ด๋–ป๊ฒŒ ํ•˜๋ฉด ์กฐ๊ธˆ์ด๋ผ๋„ ๋” ํŽธํ•˜๊ฒŒ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์„๊นŒ ๊ณ ๋ฏผํ•˜๋‹ค๊ฐ€ ์•„์ฃผ ์ข‹์€ ์นœ๊ตฌ๋ฅผ ๋ฐœ๊ฒฌํ–ˆ์Šต๋‹ˆ๋‹ค.

๋ฐ”๋กœ Property Wrapper๋ผ๋Š” ์นœ๊ตฌ์ธ๋ฐ์š”.

์ด ์นœ๊ตฌ๋Š” SwiftUI์™€ Combine์˜ ๊ธฐ๋Šฅ์„ UIKit์—์„œ๋„ ์ ์šฉํ•  ์ˆ˜ ์žˆ๋„๋ก Swift 5.1๋ฒ„์ „์— ๋“ฑ์žฅํ–ˆ์Šต๋‹ˆ๋‹ค.

๋งŽ์€ ๊ฒŒ์‹œ๋ฌผ์—์„œ SwiftUI์˜ @State๋ฅผ ์˜ˆ์‹œ๋กœ ๋“ค๋ฉฐ ์„ค๋ช…ํ•˜๊ณ  ์žˆ๋Š”๋ฐ, ์ €๋Š” ๊ฒฝํ—˜์ด ์—†์–ด์„œ.. ๋„˜์–ด๊ฐ€๊ตฌ์š”โ€ฆ

์ด๋ฆ„์„ ๋ณด๋ฉด ํ”„๋กœํผํ‹ฐ๋ฅผ ๊ฐ์‹ธ์„œ ๋ญ”๊ฐ€๋ฅผ ํ•˜๋Š” ์นœ๊ตฌ์ธ ๊ฒƒ ๊ฐ™์ฃ ?

๋ฐ”๋กœ ์•Œ์•„๋ณด๋„๋ก ํ•˜๊ฒ ์Šต๋‹ˆ๋‹ค.

์šฉ๋„

์ผ๋‹จ ์–ด๋–ค ๊ฐœ๋…์œผ๋กœ ์‚ฌ์šฉ๋˜๋Š” ๊ฑด์ง€๋ถ€ํ„ฐ ์•Œ์•„๋ด…์‹œ๋‹ค.

Swift์—๋Š” lazy ํ”„๋กœํผํ‹ฐ ๋ผ๋Š” ๊ฒƒ์ด ์žˆ์Šต๋‹ˆ๋‹ค.

ํ•ด๋‹น ํ”„๋กœํผํ‹ฐ๊ฐ€ ์‚ฌ์šฉ๋  ๋•Œ ๋กœ๋“œ๋ฅผ ์‹œ์ž‘ํ•˜๋„๋ก ํ•ด์ฃผ๋Š” ํ‚ค์›Œ๋“œ์ด์ฃ .

์ด lazy ํ‚ค์›Œ๋“œ๋Š” ๋ชจ๋“  ํ‚ค์›Œ๋“œ๋“ค์— ๋ถ™์ผ ์ˆ˜ ์žˆ๊ณ  ๋™์ผํ•œ ๋™์ž‘์„ ํ•ฉ๋‹ˆ๋‹ค.

๋งŒ์•ฝ ์ด ํ‚ค์›Œ๋“œ๊ฐ€ ์—†๋‹ค๋ฉด ํ•ด๋‹น ๊ธฐ๋Šฅ์„ ๊ตฌํ˜„ํ•˜๊ธฐ ์œ„ํ•ด์„œ ์–ด๋–ค ๊ณผ์ •์„ ๋ฐŸ๊ฒŒ ๋ ๊นŒ์š”?

func getMyClass() -> MyClass {
  if self.myVar == nil {
    self.myVar = MyClass()
  }
  return self.myVar
}

์œ„์™€ ๊ฐ™์ด ํ•ด๋‹น ํ”„๋กœํผํ‹ฐ๊ฐ€ ์ดˆ๊ธฐํ™”๊ฐ€ ๋๋Š”์ง€ ํ™•์ธ์„ ํ•œ ํ›„์— ๊ฐ’์„ ๋ถˆ๋Ÿฌ์˜ค๋Š” ํ•จ์ˆ˜๋ฅผ ๋งŒ๋“ค์–ด ๋ชจ๋“  ํ”„๋กœํผํ‹ฐ์— ์ง์ ‘ ๊ตฌํ˜„ํ•˜๋ฉฐ ์ ์šฉ์‹œ์ผœ์ฃผ์–ด์•ผ ํ•  ๊ฑฐ์—์š”.

๋‹คํ–‰ํžˆ๋„ ์œ„ ๋™์ž‘์„ ์šฐ๋ฆฌ๋Š”

lazy var myClass = MyClass()

์ด๋ ‡๊ฒŒ ๊ฐ„ํŽธํ•˜๊ฒŒ ๊ตฌํ˜„ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

Property Wrapper๋Š” lazy์™€ ๊ฐ™์ด ๋™์ผํ•œ ๋™์ž‘์„ ํ•˜๋Š” ํ”„๋กœํผํ‹ฐ๋“ค์— ๋Œ€ํ•ด์„œ ๊ณตํ†ต์ ์œผ๋กœ ๋ฏธ๋ฆฌ ๊ตฌํ˜„๋œ ๋™์ž‘๋“ค์„ ๋ถ€์—ฌํ•  ์ˆ˜ ์žˆ๋Š” ๊ธฐ๋Šฅ์ž…๋‹ˆ๋‹ค.

์ •์˜

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

์˜ˆ์‹œ๋กœ UserDefaults๋กœ ์ž๋™์œผ๋กœ ๋™๊ธฐํ™” ์ž‘์—…์„ ํ•˜๋Š” ํ”„๋กœํผํ‹ฐ๋ฅผ ๋งŒ๋“ค์–ด๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค.

@propertyWrapper
struct UserDeafult<T> {
  var key: String
  var defaultValue: T
}

๊ทธ๋Ÿฐ๋ฐ ์ด๋ ‡๊ฒŒ๊นŒ์ง€๋งŒ ํ•˜๋ฉด ํ•ด๋‹น ํ”„๋กœํผํ‹ฐ์— ๋ณ€๊ฒฝ์ด ์žˆ์„ ๋•Œ๋งˆ๋‹ค ์–ด๋–ค ์ž‘์—…์ด ์‹คํ–‰๋˜๋Š”์ง€์— ๋Œ€ํ•œ ๋ช…์‹œ๊ฐ€ ์—†์ฃ .

๊ทธ๋ž˜์„œ ์—๋Ÿฌ๊ฐ€ ๋ฐœ์ƒํ•˜๊ณ , ๊ทธ ์—๋Ÿฌ๋ฅผ ์‚ดํŽด๋ณด๋ฉด wrappedValue๊ฐ€ ์ •์˜๋˜์ง€ ์•Š์•˜๋‹ค๋Š” ๊ฒƒ์„ ์•Œ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

wrappedValue๋ฅผ ์ •์˜ํ•ด์ฃผ๊ณ , get-set์„ ์‚ฌ์šฉํ•˜์—ฌ ํ•ด๋‹น ํ”„๋กœํผํ‹ฐ๊ฐ€ ๋ณ€๊ฒฝ๋  ๋•Œ๋งˆ๋‹ค ๋ฐœ์ƒํ•˜๋Š” ๋กœ์ง์„ ๋„ฃ์–ด์ค๋‹ˆ๋‹ค.

var wrappedValue: T {
  get {
    UserDefaults.standard.object(forKey: key) as? T ?? defaultValue
  }
  set {
    UserDefaults.standard.set(newValue, forKey: key)
  }
}

์ด์ œ ํ•ฉ์ณ๋ณผ๊นŒ์š”?

@propertyWrapper
struct UserDefault<T> {
  var key: String
  var defaultValue: T

  var wrappedValue: T {
    get {
      UserDefaults.standard.object(forKey: key) as? T ?? defaultValue
    }
    set {
      UserDefaults.standard.get(newValue, forKey: key)
    }
  }
}

์‚ฌ์šฉ

์ž ๊ทธ๋Ÿผ ์ ์šฉ์€ ์–ด๋–ป๊ฒŒ ํ• ๊นŒ์š”?

lazy์™€ ๋งˆ์ฐฌ๊ฐ€์ง€๋กœ ์•ž์— ๋ช…์‹œํ•ด์ฃผ๋ฉด ๋ฉ๋‹ˆ๋‹ค.

struct AppData {
  @UserDefault(key: "isLoggedIn", defaultValue: false)
  static var isLoggedIn: Bool

  @UserDefault(key: "username", defaultValue: "")
  static var username: String
}

AppData.isLoggedIn = true
print(AppData.isLoggedIn) // true

print(AppData.username) // ""

์กฐ๊ธˆ ๋” ๋‚˜์•„๊ฐ€์„œ ์ปค์Šคํ…€ ํƒ€์ž…์—๋„ ์ ์šฉ๋  ์ˆ˜ ์žˆ๋„๋ก ๋ฐ”๊ฟ”๋ด…์‹œ๋‹ค.

@propertyWrapper
struct UserDefault<T: Codable> {
  // key์™€ default ๊ฐ’์€ ๋ฐ”๋€” ์ผ์ด ์—†์œผ๋‹ˆ let์œผ๋กœ ๋ณ€๊ฒฝ
  private let key: String
  private let defaultValue: T

  init(key: String, defaultValue: T) {
    self.key = key
    self.defaultValue = defaultValue
  }

  var wrappedValue: T {
    get {
      guard let data = Userdefaults.standard.object(forKey: key) as? Data else {
        return defaultValue
      }
      let value = try? JSONDecoder().decode(T.self, from: data)
      return value ?? defaultValue
    }
    set {
      let data = try? JSONEncoder().encode(newValue)
      UserDefaults.standard.set(data, forKey: key)
    }
  }
}

์ด๋ ‡๊ฒŒ ์ž๋™์œผ๋กœ UserDefaults๋กœ ๊ฐ’์„ ๋™๊ธฐํ™”์‹œ์ผœ์ฃผ๋Š” Property Wrapper๋ฅผ ๋งŒ๋“ค๊ณ  ์ ์šฉํ•ด๋ณด์•˜์Šต๋‹ˆ๋‹ค.

์ฐธ๊ณ  ๊ฒŒ์‹œ๋ฌผ

# Create the Perfect UserDefaults Wrapper Using Property Wrapper