Skip to content

Instantly share code, notes, and snippets.

@MaximKotliar
Last active January 29, 2020 18:21
Show Gist options
  • Save MaximKotliar/997984ea77abba87303b60254cabc1cc to your computer and use it in GitHub Desktop.
Save MaximKotliar/997984ea77abba87303b60254cabc1cc to your computer and use it in GitHub Desktop.
//
// UserDefaultsManagedWrapper.swift
// Yoga
//
// Created by Maxim Kotliar on 11.12.2019.
// Copyright © 2019 Wikrgroup. All rights reserved.
//
import Foundation
import Bindy
@propertyWrapper
class UserDefaultsManaged<T: Codable> {
let key: String
let defaultValue: T
let userDefaults: UserDefaults
let projectedValue: Observable<T>
private var notificationToken: NSObjectProtocol?
init(key: String, defaultValue: T, userDefaults: UserDefaults = .standard) {
func currentValue() -> T {
userDefaults.value(for: key) ?? defaultValue
}
self.key = key
self.defaultValue = defaultValue
self.userDefaults = userDefaults
self.projectedValue = Observable(currentValue())
var skipNextChange: Bool = false
projectedValue.observe(self) { value in
guard !skipNextChange else { return }
userDefaults.set(value: value, forKey: key)
}
notificationToken =
NotificationCenter.default.addObserver(forName: UserDefaults.didChangeNotification,
object: nil,
queue: nil) { [weak self] _ in
skipNextChange = true
self?.projectedValue.value = currentValue()
skipNextChange = false
}
}
var wrappedValue: T {
get {
return self.projectedValue.value
}
set {
projectedValue.value = newValue
}
}
}
extension UserDefaults {
private struct CodableWrapper<T: Codable>: Codable {
let value: T
}
func value<T: Codable>(for key: String) -> T? {
do {
guard let data = self.data(forKey: key) else { return nil }
return try JSONDecoder.fromLocalStorage.decode(CodableWrapper<T>.self, from: data).value
} catch {
return nil
}
}
func set<T: Codable>(value: T?, forKey key: String) {
do {
guard let value = value else {
removeObject(forKey: key)
return
}
let data = try JSONEncoder.toLocalStorage.encode(CodableWrapper(value: value))
set(data, forKey: key)
} catch {
assertionFailure("Fail to store \(T.self) in keychain, reason: \(error.localizedDescription)")
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment