Store: save one Fuel Savings field per edit, and update keychain items in place
The Settings sliders called persistFuelSavingsSettings() on every value change, which rewrote all four keys — four UserDefaults writes and four keychain round trips per step, on the main thread, up to 90 times per drag. That is what made them stutter. saveFuelSavingsField(_:from:) writes just the field that moved, so a slider gesture touches one key. writeKeychain now uses SecItemUpdate and only falls back to SecItemAdd on errSecItemNotFound. Delete-then-add cost two round trips to securityd and briefly left the key holding no value at all — a window where the keychain, the authoritative mirror on read, could disagree with the app-group defaults. Tests pin the store half: a single-field save leaves the other three keys alone, and the whole-settings save still writes all four.
This commit is contained in:
+29
-2
@@ -850,6 +850,26 @@ struct FuelStore {
|
||||
saveString(settings.preferredAmount.unit.rawValue, service: fuelSavingsUnitKey)
|
||||
}
|
||||
|
||||
/// One field of the Fuel Savings settings. A control that edits a single
|
||||
/// value saves just that field, so moving the MPG slider rewrites the MPG key
|
||||
/// instead of all four — four UserDefaults writes and four keychain round
|
||||
/// trips per gesture is what made those sliders stutter.
|
||||
enum FuelSavingsField {
|
||||
case enabled
|
||||
case mpg
|
||||
case amount
|
||||
case unit
|
||||
}
|
||||
|
||||
static func saveFuelSavingsField(_ field: FuelSavingsField, from settings: FuelSavingsSettings) {
|
||||
switch field {
|
||||
case .enabled: saveString(settings.enabled ? "1" : "0", service: fuelSavingsEnabledKey)
|
||||
case .mpg: saveString(String(settings.mpg), service: fuelSavingsMPGKey)
|
||||
case .amount: saveString(String(settings.preferredAmount.value), service: fuelSavingsAmountKey)
|
||||
case .unit: saveString(settings.preferredAmount.unit.rawValue, service: fuelSavingsUnitKey)
|
||||
}
|
||||
}
|
||||
|
||||
/// Superscript digit glyphs for the pounds & pence format's small raised
|
||||
/// third digit (the forecourt style: "£1.29⁹").
|
||||
private static let superscriptDigits: [Character] = ["⁰", "¹", "²", "³", "⁴", "⁵", "⁶", "⁷", "⁸", "⁹"]
|
||||
@@ -1719,11 +1739,18 @@ struct FuelStore {
|
||||
}
|
||||
|
||||
private static func writeKeychain(data: Data, service: String) {
|
||||
let deleteQuery: [String: Any] = [
|
||||
// Update in place when the item already exists. Delete-then-add cost two
|
||||
// round trips to securityd and briefly left the key holding no value at
|
||||
// all, which is half of why a setting could disagree between launches.
|
||||
let query: [String: Any] = [
|
||||
kSecClass as String: kSecClassGenericPassword,
|
||||
kSecAttrService as String: service,
|
||||
]
|
||||
SecItemDelete(deleteQuery as CFDictionary)
|
||||
let status = SecItemUpdate(query as CFDictionary,
|
||||
[kSecValueData as String: data] as CFDictionary)
|
||||
guard status == errSecItemNotFound else { return }
|
||||
// Nothing stored under this service yet — the first write after the app
|
||||
// was deleted, or a key added since. Add it.
|
||||
let addQuery: [String: Any] = [
|
||||
kSecClass as String: kSecClassGenericPassword,
|
||||
kSecAttrService as String: service,
|
||||
|
||||
Reference in New Issue
Block a user