Skip to content

Instantly share code, notes, and snippets.

@shaundon
Created February 4, 2024 10:20
Show Gist options
  • Save shaundon/15935d4e3c3d6ae8aaf6674ab112528f to your computer and use it in GitHub Desktop.
Save shaundon/15935d4e3c3d6ae8aaf6674ab112528f to your computer and use it in GitHub Desktop.
Proof of concept of UserDefaults plus Zephyr, making use of notifications to update the UI when a value changes externally.
import SwiftUI
struct ContentView: View {
@State private var preferences = PreferencesModel()
var body: some View {
VStack {
HStack {
Text("Value: ")
Text(preferences.theNumber.formatted()).bold()
}
Button(action: changeNumber) {
Text("Change number")
}
}
.padding()
}
func changeNumber() {
preferences.theNumber = Int.random(in: 1...100)
}
}
#Preview {
ContentView()
}
import SwiftUI
import Zephyr
@Observable
class PreferencesModel {
init() {
/// Watch for changes to UserDefaults.
/// This will fire when a value changes externally and Zephyr updates
/// UserDefaults in kind.
let observer = NotificationCenter.default.addObserver(
forName: UserDefaults.didChangeNotification,
object: nil,
queue: nil,
using: handleUpdatedUserDefaults
)
Zephyr.debugEnabled = true
Zephyr.sync(keys: UserPreferences.Keys.allKeyValues)
Zephyr.addKeysToBeMonitored(keys: UserPreferences.Keys.allKeyValues)
}
/// See what changed in UserDefaults and update our model in kind.
/// This means that when a value changes externally, the UI will update automatically.
private func handleUpdatedUserDefaults(from notification: Notification) {
guard let updatedUserDefaults = notification.object as? UserDefaults else {
return
}
let updatedNumber = updatedUserDefaults.integer(forKey: UserPreferences.Keys.theNumber.rawValue)
if updatedNumber != self.theNumber {
self.theNumber = updatedNumber
}
}
/// Observable var directly linked to UserDefaults.
/// When it's set from a view (e.g. in ContentView), UserDefaults is automatically updated to match.
var theNumber = UserPreferences.theNumber {
didSet { UserPreferences.theNumber = self.theNumber }
}
}
struct UserPreferences {
/// Convenience enum for noting all the keys we store in UserDefaults.
enum Keys: String, CaseIterable {
case theNumber
static var allKeyValues: [String] {
Self.allCases.map { $0.rawValue }
}
}
/// Standard value stored in UserDefaults.
static var theNumber: Int {
get { return UserDefaults.standard.integer(forKey: Keys.theNumber.rawValue) }
set { UserDefaults.standard.set(newValue, forKey: Keys.theNumber.rawValue) }
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment