Skip to content

Instantly share code, notes, and snippets.

@IanKeen
Last active June 11, 2024 08:35
Show Gist options
  • Save IanKeen/4d29b48519dca125b21675eeb7623d60 to your computer and use it in GitHub Desktop.
Save IanKeen/4d29b48519dca125b21675eeb7623d60 to your computer and use it in GitHub Desktop.
PropertyWrapper: Storage to extend support for more types using `@AppStorage`
// RawRepresentable types using Double aren't supported by default by AppStorage
enum Foo: Double, AppStorageConvertible {
case a = 1.0
case b = 1.5
}
struct MyView: View {
@AppStorage("foo") @Storage private var foo: Foo = .a
var body: some View {
Text("\(foo.rawValue)")
Button("A") { foo = .a }
Button("B") { foo = .b }
}
}
struct MyView: View {
// [String] isn't supported by default by AppStorage, `@Storage` supports it for free
@AppStorage("foo") @Storage private var foo: [String] = []
var body: some View {
Text(foos.joined(separator: ","))
Button("A") { foos.append("A") }
Button("B") { foos.append("b") }
}
}
@propertyWrapper
struct Storage<T: AppStorageConvertible>: RawRepresentable {
var rawValue: String { wrappedValue.storedValue }
var wrappedValue: T
init?(rawValue: String) {
guard let value = T.init(rawValue) else { return nil }
self.wrappedValue = value
}
init(wrappedValue: T) {
self.wrappedValue = wrappedValue
}
}
extension Binding {
func binding<T>() -> Binding<T> where Value == Storage<T> {
return .init(
get: { wrappedValue.wrappedValue },
set: { value, transaction in
self.transaction(transaction).wrappedValue.wrappedValue = value
}
)
}
}
protocol AppStorageConvertible {
init?(_ storedValue: String)
var storedValue: String { get }
}
extension RawRepresentable where RawValue: LosslessStringConvertible, Self: AppStorageConvertible {
init?(_ storedValue: String) {
guard let value = RawValue(storedValue) else { return nil }
self.init(rawValue: value)
}
var storedValue: String {
String(describing: rawValue)
}
}
extension Array: AppStorageConvertible where Element: LosslessStringConvertible {
public init?(_ storedValue: String) {
let values = storedValue.components(separatedBy: ",")
self = values.compactMap(Element.init)
}
var storedValue: String {
return map(\.description).joined(separator: ",")
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment