Created
August 10, 2025 15:20
-
-
Save megabitsenmzq/c6547191e80c65c253b11a7ce1eee086 to your computer and use it in GitHub Desktop.
AIO ObservableUserDefault PropertyWrapper
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| // | |
| // UserDefaultWrapper.swift | |
| // Code Drawer | |
| // | |
| // Created by Jinyu Meng on 2023/7/23. | |
| // | |
| import Combine | |
| import SwiftUI | |
| @propertyWrapper | |
| public struct ObservableUserDefault<T: Sendable> { | |
| private let get: () -> T | |
| private let set: (T) -> Void | |
| public var wrappedValue: T { | |
| get { | |
| get() | |
| } | |
| nonmutating set { | |
| set(newValue) | |
| } | |
| } | |
| public init(wrappedValue value: T, key: String, container: UserDefaults = .standard) { | |
| get = { | |
| return container.object(forKey: key) as? T ?? value | |
| } | |
| set = { newValue in | |
| container.set(newValue, forKey: key) | |
| } | |
| } | |
| public init(wrappedValue value: T, key: String, container: UserDefaults = .standard) where T: RawRepresentable { | |
| get = { | |
| if let rawValue = container.object(forKey: key) as? T.RawValue { | |
| return T(rawValue: rawValue) ?? value | |
| } else { | |
| return value | |
| } | |
| } | |
| set = { newValue in | |
| container.set(newValue.rawValue, forKey: key) | |
| } | |
| } | |
| public init(wrappedValue value: T, key: String, container: UserDefaults = .standard) where T: Codable { | |
| get = { | |
| let decoder = JSONDecoder() | |
| if let data = container.data(forKey: key) { | |
| return (try? decoder.decode(T.self, from: data)) ?? value | |
| } else { | |
| return value | |
| } | |
| } | |
| set = { newValue in | |
| let encoder = JSONEncoder() | |
| if let data = try? encoder.encode(newValue) { | |
| container.set(data, forKey: key) | |
| } else { | |
| container.removeObject(forKey: key) | |
| } | |
| } | |
| } | |
| public init(wrappedValue value: T, key: String, container: UserDefaults = .standard) where T: Codable & RawRepresentable { | |
| get = { | |
| let decoder = JSONDecoder() | |
| if let data = container.data(forKey: key) { | |
| return (try? decoder.decode(T.self, from: data)) ?? value | |
| } else { | |
| return value | |
| } | |
| } | |
| set = { newValue in | |
| let encoder = JSONEncoder() | |
| if let data = try? encoder.encode(newValue) { | |
| container.set(data, forKey: key) | |
| } else { | |
| container.removeObject(forKey: key) | |
| } | |
| } | |
| } | |
| @MainActor | |
| public static subscript<EnclosingSelf: AnyObject> ( | |
| _enclosingInstance object: EnclosingSelf, | |
| wrapped wrappedKeyPath: ReferenceWritableKeyPath<EnclosingSelf, T>, | |
| storage storageKeyPath: ReferenceWritableKeyPath<EnclosingSelf, ObservableUserDefault<T>> | |
| ) -> T { | |
| get { | |
| return object[keyPath: storageKeyPath].wrappedValue | |
| } | |
| set { | |
| if let object = object as? any ObservableObject { | |
| DispatchQueue.main.async { | |
| (object.objectWillChange as? ObservableObjectPublisher)?.send() | |
| } | |
| } | |
| object[keyPath: storageKeyPath].wrappedValue = newValue | |
| } | |
| } | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment