-
-
Save iamsatar/fb713b0325e59538721569e499f1a030 to your computer and use it in GitHub Desktop.
This file contains 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
// | |
// Persisting.swift | |
// | |
// Created by James Sedlacek on 1/30/25. | |
// | |
import SwiftUI | |
/// A property wrapper that manages the persistence of Codable types using UserDefaults. | |
/// | |
/// The `Persisting` property wrapper provides a convenient way to persist Codable types | |
/// in UserDefaults by automatically handling the encoding and decoding process. | |
/// | |
/// # Usage Example | |
/// ```swift | |
/// enum TimeFormat: String, Codable, Hashable { | |
/// case twelveHour = "12h" | |
/// case twentyFourHour = "24h" | |
/// } | |
/// | |
/// struct SettingsView: View { | |
/// @Persisting("timeFormat") private var format = TimeFormat.twelveHour | |
/// | |
/// var body: some View { | |
/// Picker("Time Format", selection: $format) { | |
/// Text("12 Hour").tag(TimeFormat.twelveHour) | |
/// Text("24 Hour").tag(TimeFormat.twentyFourHour) | |
/// } | |
/// } | |
/// } | |
/// ``` | |
@MainActor | |
@propertyWrapper | |
public struct Persisting<T: Codable & Hashable>: DynamicProperty { | |
@AppStorage private var storage: Data | |
private let defaultValue: T | |
private let encoder: JSONEncoder = .init() | |
private let decoder: JSONDecoder = .init() | |
/// The wrapped value of the Codable type. | |
public var wrappedValue: T { | |
get { | |
guard let decoded = try? decoder.decode(T.self, from: storage) else { | |
return defaultValue | |
} | |
return decoded | |
} | |
nonmutating set { | |
guard let encoded = try? encoder.encode(newValue) else { return } | |
storage = encoded | |
} | |
} | |
/// A binding to the Codable value stored in `UserDefaults`. | |
public var projectedValue: Binding<T> { | |
Binding( | |
get: { wrappedValue }, | |
set: { wrappedValue = $0 } | |
) | |
} | |
/// Initializes the property wrapper. | |
/// | |
/// - Parameters: | |
/// - wrappedValue: The default value if no value is found in UserDefaults. | |
/// - key: The key used to store the value in UserDefaults. | |
/// - store: The UserDefaults store to use. Defaults to `.standard`. | |
public init( | |
wrappedValue: T, | |
_ key: String, | |
store: UserDefaults? = nil | |
) { | |
defaultValue = wrappedValue | |
let initialData = (try? encoder.encode(wrappedValue)) ?? Data() | |
_storage = .init(wrappedValue: initialData, key, store: store) | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment