Skip to content

Instantly share code, notes, and snippets.

@megabitsenmzq
Created August 10, 2025 15:20
Show Gist options
  • Select an option

  • Save megabitsenmzq/c6547191e80c65c253b11a7ce1eee086 to your computer and use it in GitHub Desktop.

Select an option

Save megabitsenmzq/c6547191e80c65c253b11a7ce1eee086 to your computer and use it in GitHub Desktop.
AIO ObservableUserDefault PropertyWrapper
//
// 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