UserDefaults 다뤄보기

이건준·2022년 12월 17일
0

문제제기

  • 나의 현재 위치를 다른 화면에서 필요한 경우가 생겨 해당 화면을 맡은 개발자분께서 UserDefaults로 직접 지정해둔 key로 값을 넘겨달라는 요청이 있었다

  • 기존에 MapCoordinate라는 위도 경도를 담는 구조체를 정의해두었고 이를 값으로 넘겨주려하였는데 에러가 뜨게 되었다

UserDefaults란

  • UserDefaults를 통해 저장한 데이터는 영구적이지못하다. 앱을 삭제하면 날라가버리는 이는 key와 value값을 이용하여 값을 저장할 수 있다

  • UserDefaults는 내부적으로 Data형으로 변환시키는 아카이브과정 Data형을 다시 기존 타입인 언아카이브과정으로 값을 가져오게된다

  • Int형, String형과 같은 경우 내부적으로 이 아카이브과정을 거치기에 UserDefaults에 아무렇지않게 값을 넣을 수 있었지만 내가 만든 커스텀 구조체같은 경우는 이러한 과정이 없어 런타임오류가 나는것이였다

해결방법

  • 앞서 말한 이 아카이브과정을 추가해주면 해결된다
let encoder = JSONEncoder()
if let currentLocation = currentLocation, let encoded = try? encoder.encode(currentLocation) {
                UserDefaults.standard.set(encoded, forKey: .currentLocation)
}
if let savedData = UserDefaults.standard.object(forKey: .currentLocation) as? Data {
	let decoder = JSONDecoder()
if let savedObject = try? decoder.decode(MapCoordinate.self, from: savedData) {
	print("savedObject = \(savedObject)")
    }
}
  • 위같은 코드를 추가하여 encode, decode과정을 거쳐서 가져온다면 오류를 해결해줄 수 있다

해결방법 2

  • 계속 위 해결방법처럼 구조체와 같이 아카이빙, 언아카이빙과정을 일일이 코딩을 하기엔 비효율적이라 생각하여 찾아보니 @propertyWrapper라는 것을 찾게 되었다

  • 위 어노테이션을 지정해주면 wrappedValue에 get set을 통해 위 과정을 정의하고 해당 구조체 이름을 어노테이션으로 지정해주어 사용해주면 되는데 코드로 직접 확인해보자

public struct UserDefaultsManager {
    @UserDefaultWrapper(key: .currentLocation, defaultValue: ["lat": 30.000, "lng": 30.000])
    static var currentLocation
}

@propertyWrapper
fileprivate struct UserDefaultWrapper<T: Codable> {
    private let key: UserDefaultsKeys
    private let defaultValue: T

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

    var wrappedValue: T? {
        get {
            if let savedData = UserDefaults.standard.object(forKey: key) as? Data {
                let decoder = JSONDecoder()
                if let lodedObejct = try? decoder.decode(T.self, from: savedData) {
                    return lodedObejct
                }
            }
            return defaultValue
        }
        set {
            let encoder = JSONEncoder()
            if let encoded = try? encoder.encode(newValue) {
                UserDefaults.standard.set(object: encoded, forKey: key)
            }
        }
    }
}
  • 위 코드에서 보이듯이 defaultValue를 넣을 수 있고 get에는 아카이빙 과정을, set에는 언아카이빙과정을 코딩해줌으로써 편리하게 사용이 가능하다
UserDefaultsManager.currentLocation = currentLocation
  • 위처럼 그냥 UserDefaultsManager를 통해 값만 넣어주면 내부적으로 아카이빙과정을 도와주어 메모리나 디스크에 저장할 수 있는 byte형식으로 저장되게 된다

0개의 댓글