나의 현재 위치를 다른 화면에서 필요한 경우가 생겨 해당 화면을 맡은 개발자분께서 UserDefaults로 직접 지정해둔 key로 값을 넘겨달라는 요청이 있었다
기존에 MapCoordinate라는 위도 경도를 담는 구조체를 정의해두었고 이를 값으로 넘겨주려하였는데 에러가 뜨게 되었다
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)")
}
}
계속 위 해결방법처럼 구조체와 같이 아카이빙, 언아카이빙과정을 일일이 코딩을 하기엔 비효율적이라 생각하여 찾아보니 @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)
}
}
}
}
UserDefaultsManager.currentLocation = currentLocation