티스토리 뷰

안녕하세요.

UserDefault를 사용할 일이 있었는데 기존에는 아래처럼 연산 프로퍼티로 구성해서 사용하고 있었습니다.

    var token: String? {
        get {
            return UserDefaults.standard.string(forKey: UserDefaultKey.token)
        }
        set {
            UserDefaults.standard.set(newValue, forKey: UserDefaultKey.token)
            UserDefaults.standard.synchronize()
        }
    }
    
    var adminCd: Int? {
        get {
            return UserDefaults.standard.integer(forKey: UserDefaultKey.adminCd)
        }
        set {
            UserDefaults.standard.set(newValue, forKey: UserDefaultKey.adminCd)
            UserDefaults.standard.synchronize()
        }
    }

쓰다 보니까 get set 내부의 UserDefaultKey와 get부분의 integer 같은 가져오는 데이터 타입 빼고는 다 같은 구문이라 이걸 공통화할 수 없을까 해서 찾아보다가 Swift 5.1부터 추가된 propertyWrapper를 알게 되었습니다.

 

propertyWrapper는 연산 프로퍼티의 연산 부분을 공통화하여 중복 코드를 줄여주는 기능입니다.

 

위의 코드를 propertyWrapper를 사용하면 아래와 같은 형태가 됩니다.

    @UserDefault(key: UserDefaultKey.token, defaultValue: "")
    var name: String
    
    @UserDefault(key: UserDefaultKey.Int, defaultValue: 0)
    var name: Int

처음에 만들었던 두 개의 연산프로퍼티에서 중복되지 않은 값은 자료형과 UserDefault에 저장되는 key값이었는데, 이 두개의 값만 @UserDefault라는 어노테이션에 인자 값으로 전달되고 있습니다.

 

나머지 공통 코드가 UserDefault라는 propertyWrapper에 작성되어서 재사용되는 형태입니다.

@propertyWrapper
struct UserDefault<T: Codable> {
    let key: String
    let defaultValue: T
    
    var wrappedValue: T {
        get {
            do {
                let data = UserDefaults.standard.data(forKey: self.key)
                return try JSONDecoder().decode(T.self, from: data ?? Data())
            } catch {
                print(error.localizedDescription)
            }
            return defaultValue
        }
        set {
            do {
                let jsonEncoder = JSONEncoder()
                UserDefaults.standard.set(try jsonEncoder.encode(newValue), forKey: self.key)
                UserDefaults.standard.synchronize()
            }
            catch {
                print(error.localizedDescription)
            }
        }
    }
}

먼저 UserDefault 구조체를 만들고 @propertyWrapper 어노테이션을 붙여주어 프로퍼티래퍼로 선언해줍니다.

내부에는 key, defaultValue, wrappedValue 변수가 있는데 wrappedValue는 필수이며 나머지는 편의를 위해 추가했습니다.

 

UserDefaults에 값을 저장하고 가져오는 작업에 필요한 인코딩 디코딩을 위해 제네릭을 사용해 Codable을 채택한 객체로 타입을 제한했고 wrappedValue가 T타입이기 때문에 프로퍼티래퍼를 사용하는 변수의 데이터 타입이 Codable을 채택한 타입이어야 합니다.

 

wrappedValue의 set을 살펴보면 새로 할당된 값을 JSONEncoder로 json타입으로 변환하여 UserDefault에 저장하고

get에서는 UserDefaults에서 key에 해당하는 값을 data타입으로 가져와 JSONDecoder를 통해 커스텀 타입(T.self)으로  변환하여 리턴하고 만약 변환이 실패하면 미리 지정한 defaultValue를 리턴합니다.

 

이러한 작업을 통해서 중복되는 코드를 최대한 줄이고 이후 동일한 작업을 하는 프로퍼티가 추가되더라도 간단한 코드 작성만으로 기능을 구현할 수 있어 편리합니다.

 

이번 예제에서는 UserDefault 작업을 예로 들었지만 연산 프로퍼티에서 공통화할 부분이 있다면 어느 기능에서나 propertyWrapper의 이용이 가능합니다.

 

댓글