PropertyWrapper란 무엇일까 ??

이건준·2023년 4월 15일
0

문제제기

  • PLUB이라는 앱을 개발하면서 검색Input에 대해 최근검색어리스트가 존재할 경우 화면을 나갔다가 들어가더라도 유지되는 상황을 개발하여야했다

  • 이 상황에서 UserDefaults를 이용해야겠다는 생각이 들었는데 그러면서 PropertyWrapper라는 것을 알게되었다, 이게 무엇일까 ??

var currentPage: Int {
    get {
      return self._currentPage
    }
    set {
      if self._currentPage == newValue { return }
      
      if newValue >= numberOfPages {
        self._currentPage = numberOfPages - 1
      }
      else if newValue < 0 {
        self._currentPage = 0
      }
      else {
        self._currentPage = newValue
      }
    }
  • 우리는 프로퍼티를 사용하게 될때 위처럼 get, set을 이용해서 프로퍼티에 대한 연산을 할 수 있게된다
  • 그렇다면 다음과 같은 공통된 연산이 있다면 모든 프로퍼티마다 get, set을 위와 같이 구현해주어야할것이다

이때 필요한것이 @propertyWrapper이다

static var accessToken: String? {
        get { return UserDefaults.standard.string(forKey: "accessToken") }
        set { UserDefaults.standard.set(newValue, forKey: "accessToken") }
    }
  • 우리가 만약에 위와 같이 UserDefaults.standard를 이용해서 값을 넣고 빼내오는 동작을 할때 위처럼 get set으로 정의할 수 있을 것이다, 한번 이를 @propertyWrapper를 이용해서 고쳐보자
@propertyWrapper
struct UserDefaultsWrapper<T: Codable> {
    
  private let key: String
  
  init(key: String) {
    self.key = key
  }
  
  var wrappedValue: T? {
    get {
      guard let data = UserDefaults.standard.object(forKey: self.key) as? Data,
            let decodedData = try? JSONDecoder().decode(T.self, from: data) else {
        return nil
      }
      return decodedData
    }
    set {
      guard let data = try? JSONEncoder().encode(newValue)
      else {
        UserDefaults.standard.removeObject(forKey: key)
        return
      }
      UserDefaults.standard.setValue(data, forKey: key)
    }
  }
}
  • @propertyWrapper로 선언해주면 반드시 wrappedValue를 구현해주어야하는데 여기다가 공통으로 작업하고싶은 get set에 대한 로직을 구현해주면 된다

  • UserDefaultsWrapper라는 말처럼 그대로 특정 역할을 위한 Wrapper를 만드는것이다

그럼 사용법은 어떻게 될까 ??

@UserDefaultsWrapper<[String]>(key: "accessToken")
  private(set) var accessToken
  • 위처럼 사용가능하여 로직을 일일이 작성할 필요가 없어진다

주의할 점 !!!

  • 위 코드에서 accessToken을 그대로 사용할 경우 nil이 되는데 디폴트 값이 필요한 경우에 nil은 추가코드를 유발하게 된다

  • 이와 같은 경우는 어떻게 해야할까 ??

@propertyWrapper
struct UserDefaultWrapper<T> {
    
    let key: String
    let defaultValue: T

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

@UserDefaultWrapper(key: "accessToken", defaultValue: "")
    static var accessToken: String
  • 이처럼 디폴트 값을 이용해서 제네릭타입을 지정해줄 수도 있고 기본값을 설정해 nil을 방지해줄 수도 있다 !!

0개의 댓글