Skip to content

Instantly share code, notes, and snippets.

@davidsteppenbeck
Last active January 30, 2023 10:15
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save davidsteppenbeck/6da24b22e1b0acaa3d03249d04b24f39 to your computer and use it in GitHub Desktop.
Save davidsteppenbeck/6da24b22e1b0acaa3d03249d04b24f39 to your computer and use it in GitHub Desktop.
A Swift property wrapper for storing colors in User Defaults.
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