diff --git a/src/logic/DebugProfile.swift b/src/logic/DebugProfile.swift index aea9ea2c..cbd7cc48 100644 --- a/src/logic/DebugProfile.swift +++ b/src/logic/DebugProfile.swift @@ -19,7 +19,7 @@ class DebugProfile { ("OS architecture", Sysctl.run("hw.machine")), ("Locale", Locale.current.debugDescription), ("Spaces", String(Spaces.allIdsAndIndexes().count)), - ("Dark mode", Preferences.getString("AppleInterfaceStyle") ?? "Light"), + ("Dark mode", defaults.string(forKey: "AppleInterfaceStyle") ?? "Light"), ("\"Displays have separate Spaces\"", NSScreen.screensHaveSeparateSpaces ? "checked" : "unchecked"), // hardware ("Hardware model", Sysctl.run("hw.model")), diff --git a/src/logic/Preferences.swift b/src/logic/Preferences.swift index 5fbc8ffe..00e9119d 100644 --- a/src/logic/Preferences.swift +++ b/src/logic/Preferences.swift @@ -151,7 +151,7 @@ class Preferences { defaults.string(forKey: key) } - static func set(_ key: String, _ value: Any?) { + static func set(_ key: String, _ value: String) { defaults.set(value, forKey: key) UserDefaults.cache.removeValue(forKey: key) } @@ -436,39 +436,50 @@ enum CrashPolicyPreference: String, CaseIterable, MacroPreference { } extension UserDefaults { - static var cache = [String: Any]() + static var cache = [String: String]() - func getFromCacheOrFresh(_ key: String, _ getterFn: () -> T?) -> T { + func getFromCacheOrFetchAndCache(_ key: String) -> String { if let c = UserDefaults.cache[key] { - return c as! T + return c } - if let v = getterFn() { - UserDefaults.cache[key] = v - return v - } - removeObject(forKey: key) - let v = getterFn()! + let v = defaults.string(forKey: key)! UserDefaults.cache[key] = v return v } + func getThenConvertOrReset(_ key: String, _ getterFn: (String) -> T?) -> T { + let stringValue = getFromCacheOrFetchAndCache(key) + if let v = getterFn(stringValue) { + return v + } + removeObject(forKey: key) + UserDefaults.cache.removeValue(forKey: key) + let stringValue2 = getFromCacheOrFetchAndCache(key) + let v = getterFn(stringValue2)! + return v + } + func string(_ key: String) -> String { - return getFromCacheOrFresh(key, { string(forKey: key)! }) + return getFromCacheOrFetchAndCache(key) } func int(_ key: String) -> Int { - return getFromCacheOrFresh(key, { Int(string(key)) }) + return getThenConvertOrReset(key, { s in Int(s) }) } func bool(_ key: String) -> Bool { - return getFromCacheOrFresh(key, { Bool(string(key)) }) + return getThenConvertOrReset(key, { s in Bool(s) }) } func cgfloat(_ key: String) -> CGFloat { - return getFromCacheOrFresh(key, { Int(string(key)).map { CGFloat($0) } }) + return getThenConvertOrReset(key, { s in Int(s).flatMap { CGFloat($0) } }) + } + + func double(_ key: String) -> Double { + return getThenConvertOrReset(key, { s in Double(s) }) } func macroPref(_ key: String, _ macroPreferences: [A]) -> A { - return getFromCacheOrFresh(key, { macroPreferences[safe: int(key)] }) + return getThenConvertOrReset(key, { s in Int(s).flatMap { macroPreferences[safe: $0] } }) } } diff --git a/src/ui/preferences-window/LabelAndControl.swift b/src/ui/preferences-window/LabelAndControl.swift index fde8fe34..b1c695e6 100644 --- a/src/ui/preferences-window/LabelAndControl.swift +++ b/src/ui/preferences-window/LabelAndControl.swift @@ -18,7 +18,7 @@ class LabelAndControl: NSObject { static func makeLabelWithCheckbox(_ labelText: String, _ rawName: String, extraAction: ActionClosure? = nil, labelPosition: LabelPosition = .leftWithSeparator) -> [NSView] { let checkbox = NSButton(checkboxWithTitle: labelPosition == .right ? labelText : " ", target: nil, action: nil) checkbox.translatesAutoresizingMaskIntoConstraints = false - checkbox.state = (Preferences.getString(rawName)! as NSString).boolValue ? .on : .off + checkbox.state = defaults.bool(rawName) ? .on : .off let views = makeLabelWithProvidedControl(labelText, rawName, checkbox, labelPosition: labelPosition, extraAction: extraAction) return views } @@ -30,7 +30,7 @@ class LabelAndControl: NSObject { extraAction?(textArea) } textArea.identifier = NSUserInterfaceItemIdentifier(rawName) - textArea.stringValue = Preferences.getString(rawName)! + textArea.stringValue = defaults.string(rawName) return [textArea] } @@ -42,7 +42,7 @@ class LabelAndControl: NSObject { let popUp = NSPopUpButton() popUp.translatesAutoresizingMaskIntoConstraints = false popUp.addItems(withTitles: macroPreferences.map { $0.localizedString }) - popUp.selectItem(at: Int(Preferences.getString(rawName)!)!) + popUp.selectItem(at: defaults.int(rawName)) return popUp } @@ -56,7 +56,7 @@ class LabelAndControl: NSObject { return macroPreferences.map { let button = NSButton(radioButtonWithTitle: $0.localizedString, target: nil, action: nil) button.translatesAutoresizingMaskIntoConstraints = false - button.state = Int(Preferences.getString(rawName)!)! == i ? .on : .off + button.state = defaults.int(rawName) == i ? .on : .off _ = setupControl(button, rawName, String(i), extraAction: extraAction) i += 1 return button @@ -64,12 +64,12 @@ class LabelAndControl: NSObject { } static func makeLabelWithSlider(_ labelText: String, _ rawName: String, _ minValue: Double, _ maxValue: Double, _ numberOfTickMarks: Int, _ allowsTickMarkValuesOnly: Bool, _ unitText: String = "", extraAction: ActionClosure? = nil) -> [NSView] { - let value = Preferences.getString(rawName)! - let suffixText = MeasurementFormatter().string(from: Measurement(value: Double(value)!, unit: Unit(symbol: unitText))) + let value = defaults.double(rawName) + let suffixText = MeasurementFormatter().string(from: Measurement(value: value, unit: Unit(symbol: unitText))) let slider = NSSlider() slider.minValue = minValue slider.maxValue = maxValue - slider.stringValue = value + slider.stringValue = String(value) slider.isContinuous = true return makeLabelWithProvidedControl(labelText, rawName, slider, suffixText, extraAction: extraAction) }