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:
@@ -357,6 +357,68 @@ final class AlertsFuelTests: XCTestCase {
|
||||
}
|
||||
}
|
||||
|
||||
/// The Settings sliders save ONE field per gesture instead of rewriting all four
|
||||
/// keys (four UserDefaults writes plus four keychain round trips per drag is what
|
||||
/// made them stutter). These pin the store half of that: a single-field save must
|
||||
/// leave the other three keys exactly as they were.
|
||||
final class FuelSavingsFieldSaveTests: XCTestCase {
|
||||
private func seed() {
|
||||
FuelStore.saveFuelSavingsSettings(FuelSavingsSettings(
|
||||
enabled: true,
|
||||
mpg: 40,
|
||||
preferredAmount: FuelSavingsFillAmount(value: 50, unit: .pounds)
|
||||
))
|
||||
}
|
||||
|
||||
func testSavingMPGLeavesTheOtherFieldsAlone() {
|
||||
seed()
|
||||
var edited = FuelStore.loadFuelSavingsSettings()
|
||||
edited.mpg = 62
|
||||
FuelStore.saveFuelSavingsField(.mpg, from: edited)
|
||||
let reloaded = FuelStore.loadFuelSavingsSettings()
|
||||
XCTAssertEqual(reloaded.mpg, 62, "the edited field is written")
|
||||
XCTAssertTrue(reloaded.enabled, "enabled survives an MPG save")
|
||||
XCTAssertEqual(reloaded.preferredAmount.value, 50, "the fill-up amount survives an MPG save")
|
||||
XCTAssertEqual(reloaded.preferredAmount.unit, .pounds, "the unit survives an MPG save")
|
||||
}
|
||||
|
||||
func testSavingFillAmountLeavesMPGAndUnitAlone() {
|
||||
seed()
|
||||
var edited = FuelStore.loadFuelSavingsSettings()
|
||||
edited.preferredAmount.value = 75
|
||||
FuelStore.saveFuelSavingsField(.amount, from: edited)
|
||||
let reloaded = FuelStore.loadFuelSavingsSettings()
|
||||
XCTAssertEqual(reloaded.preferredAmount.value, 75, "the edited field is written")
|
||||
XCTAssertEqual(reloaded.mpg, 40, "MPG survives a fill-up save")
|
||||
XCTAssertEqual(reloaded.preferredAmount.unit, .pounds, "the unit survives a fill-up save")
|
||||
}
|
||||
|
||||
func testSavingEnabledLeavesTheNumbersAlone() {
|
||||
seed()
|
||||
var edited = FuelStore.loadFuelSavingsSettings()
|
||||
edited.enabled = false
|
||||
FuelStore.saveFuelSavingsField(.enabled, from: edited)
|
||||
let reloaded = FuelStore.loadFuelSavingsSettings()
|
||||
XCTAssertFalse(reloaded.enabled, "the edited field is written")
|
||||
XCTAssertEqual(reloaded.mpg, 40, "MPG survives an enabled save")
|
||||
XCTAssertEqual(reloaded.preferredAmount.value, 50, "the fill-up amount survives an enabled save")
|
||||
}
|
||||
|
||||
func testWholeSettingsSaveStillWritesEveryField() {
|
||||
seed()
|
||||
FuelStore.saveFuelSavingsSettings(FuelSavingsSettings(
|
||||
enabled: false,
|
||||
mpg: 55,
|
||||
preferredAmount: FuelSavingsFillAmount(value: 30, unit: .litres)
|
||||
))
|
||||
let reloaded = FuelStore.loadFuelSavingsSettings()
|
||||
XCTAssertFalse(reloaded.enabled)
|
||||
XCTAssertEqual(reloaded.mpg, 55)
|
||||
XCTAssertEqual(reloaded.preferredAmount.value, 30)
|
||||
XCTAssertEqual(reloaded.preferredAmount.unit, .litres)
|
||||
}
|
||||
}
|
||||
|
||||
final class AlertRadiusFollowTests: XCTestCase {
|
||||
func testAlertRadiusOptionsAreMileFriendly() {
|
||||
// 1–2 city, 3 town, 5 default, 8 motorway — the geofence ceiling.
|
||||
|
||||
Reference in New Issue
Block a user