Last active
January 30, 2023 10:15
-
-
Save davidsteppenbeck/6da24b22e1b0acaa3d03249d04b24f39 to your computer and use it in GitHub Desktop.
A Swift property wrapper for storing colors in User Defaults.
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
import SwiftUI | |
@main | |
struct PropertyWrapperApp: App { | |
var body: some Scene { | |
WindowGroup { | |
VStack { | |
ContentView() | |
ContentView() | |
} | |
} | |
} | |
} | |
struct ContentView: View { | |
@ColorUserDefault(key: "COLOR_USER_DEFAULT_KEY", defaultValue: .black, store: .standard) private var color | |
@AppStorage("BOOL_USER_DEFAULT_KEY", store: .standard) private var isOn: Bool = false | |
var body: some View { | |
VStack(spacing: 20) { | |
Text("Hello, World!") | |
.foregroundStyle(color) | |
.font(.largeTitle.bold()) | |
Button("Change Color") { | |
color = .random() | |
} | |
ColorPicker(selection: $color) { | |
Text("Color Picker") | |
} | |
Toggle(isOn: $isOn) { | |
Text("Toggle Me") | |
} | |
} | |
.padding(50) | |
} | |
} | |
@propertyWrapper | |
struct ColorUserDefault: DynamicProperty { | |
private let key: String | |
private let defaultValue: Color | |
private let store: UserDefaults | |
@AppStorage private var data: Data | |
var wrappedValue: Color { | |
get { | |
if let components = try? JSONDecoder().decode(ColorComponents.self, from: data) { | |
return Color.makeFromComponents(components) | |
} else { | |
return defaultValue | |
} | |
} | |
nonmutating set { | |
if let components = newValue.getComponents() { | |
do { | |
data = try JSONEncoder().encode(components) | |
} catch { | |
data = Data() | |
} | |
} | |
} | |
} | |
var projectedValue: Binding<Color> { | |
Binding { | |
wrappedValue | |
} set: { | |
wrappedValue = $0 | |
} | |
} | |
init(key: String, defaultValue: Color, store: UserDefaults) { | |
self.key = key | |
self.defaultValue = defaultValue | |
self.store = store | |
if let data = store.data(forKey: key) { | |
_data = AppStorage(wrappedValue: data, key, store: store) | |
} else { | |
_data = AppStorage(wrappedValue: Data(), key, store: store) | |
} | |
} | |
} | |
struct ColorComponents: Codable { | |
let red: Double | |
let green: Double | |
let blue: Double | |
let alpha: Double | |
} | |
extension Color { | |
static func random() -> Color { | |
[.blue, .green, .gray, .orange, .yellow, .purple, .teal, .mint, .red, .brown, .cyan, .indigo, .pink].randomElement()! | |
} | |
static func makeFromComponents(_ components: ColorComponents) -> Color { | |
Color(UIColor(red: components.red, green: components.green, blue: components.blue, alpha: components.alpha)) | |
} | |
func getComponents() -> ColorComponents? { | |
var red: CGFloat = 0 | |
var green: CGFloat = 0 | |
var blue: CGFloat = 0 | |
var alpha: CGFloat = 0 | |
// Method `getRed` returns true if the color could be converted, false otherwise. | |
if UIColor(self).getRed(&red, green: &green, blue: &blue, alpha: &alpha) { | |
return ColorComponents(red: red, green: green, blue: blue, alpha: alpha) | |
} else { | |
return nil | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment