Browse Source

Default migrationStep to latest so fresh installs skip migrations (#631)

Previously migrationStep defaulted to 0, forcing every fresh install to
iterate through every migration. Set the default to the latest step (7)
and update other StorageValue defaults to match the post-migration final
state on a fresh install:

- snoozerPosition: .menu -> .position3
- nightscoutPosition: .position3 -> .position4
- remotePosition: .position4 -> .menu
- hasSeenFatProteinOrderChange: false -> true

Add reminder comments at the migrationStep declaration and at the
runMigrationsIfNeeded() block so future authors bump the default and
update affected defaults whenever a new step is added.
Jonas Björkert 2 months ago
parent
commit
2fa57b28c4

+ 10 - 7
LoopFollow/Storage/Storage.swift

@@ -33,7 +33,7 @@ class Storage {
     var mealWithFatProtein = StorageValue<Bool>(key: "mealWithFatProtein", defaultValue: false)
 
     // TODO: This flag can be deleted in March 2027. Check the commit for other places to cleanup.
-    var hasSeenFatProteinOrderChange = StorageValue<Bool>(key: "hasSeenFatProteinOrderChange", defaultValue: false)
+    var hasSeenFatProteinOrderChange = StorageValue<Bool>(key: "hasSeenFatProteinOrderChange", defaultValue: true)
 
     var backgroundRefreshType = StorageValue<BackgroundRefreshType>(key: "backgroundRefreshType", defaultValue: .silentTune)
 
@@ -207,7 +207,10 @@ class Storage {
     var nsWriteAuth = StorageValue<Bool>(key: "nsWriteAuth", defaultValue: false)
     var nsAdminAuth = StorageValue<Bool>(key: "nsAdminAuth", defaultValue: false)
 
-    var migrationStep = StorageValue<Int>(key: "migrationStep", defaultValue: 0)
+    // When adding a new migration step in `runMigrationsIfNeeded()`, bump this default
+    // to the new latest step number so fresh installs skip all migrations. Other defaults
+    // in this file must reflect the post-migration final state for a fresh install.
+    var migrationStep = StorageValue<Int>(key: "migrationStep", defaultValue: 7)
 
     var persistentNotification = StorageValue<Bool>(key: "persistentNotification", defaultValue: false)
     var persistentNotificationLastBGTime = StorageValue<Date>(key: "persistentNotificationLastBGTime", defaultValue: .distantPast)
@@ -219,9 +222,9 @@ class Storage {
     // Tab positions - which position each item is in (positions 1-4 are customizable, 5 is always Menu)
     var homePosition = StorageValue<TabPosition>(key: "homePosition", defaultValue: .position1)
     var alarmsPosition = StorageValue<TabPosition>(key: "alarmsPosition", defaultValue: .position2)
-    var snoozerPosition = StorageValue<TabPosition>(key: "snoozerPosition", defaultValue: .menu)
-    var nightscoutPosition = StorageValue<TabPosition>(key: "nightscoutPosition", defaultValue: .position3)
-    var remotePosition = StorageValue<TabPosition>(key: "remotePosition", defaultValue: .position4)
+    var snoozerPosition = StorageValue<TabPosition>(key: "snoozerPosition", defaultValue: .position3)
+    var nightscoutPosition = StorageValue<TabPosition>(key: "nightscoutPosition", defaultValue: .position4)
+    var remotePosition = StorageValue<TabPosition>(key: "remotePosition", defaultValue: .menu)
     var statisticsPosition = StorageValue<TabPosition>(key: "statisticsPosition", defaultValue: .menu)
     var treatmentsPosition = StorageValue<TabPosition>(key: "treatmentsPosition", defaultValue: .menu)
 
@@ -247,8 +250,8 @@ class Storage {
     /// launch, where Storage was initialized while UserDefaults was encrypted and all values were
     /// cached as their defaults.
     ///
-    /// `migrationStep` is intentionally excluded: viewDidLoad writes it to 6 during the BFU
-    /// launch; if we reloaded it and the flush had somehow not landed yet, migrations would re-run.
+    /// `migrationStep` is intentionally excluded: viewDidLoad writes it to the latest step during
+    /// the BFU launch; if we reloaded it and the flush had somehow not landed yet, migrations would re-run.
     ///
     /// SecureStorageValue properties (maxBolus, maxCarbs, maxProtein, maxFat, bolusIncrement) are
     /// not covered here — SecureStorageValue does not implement reload() and Keychain has the same

+ 6 - 0
LoopFollow/ViewControllers/MainViewController.swift

@@ -1030,6 +1030,12 @@ class MainViewController: UIViewController, UITableViewDataSource, ChartViewDele
         // Capture before migrations run: true for existing users, false for fresh installs.
         let isExistingUser = Storage.shared.migrationStep.exists
 
+        // When adding a new migration step below:
+        //   1. Bump the `migrationStep` defaultValue in Storage.swift to the new latest step
+        //      number so fresh installs skip every migration.
+        //   2. Update any other StorageValue defaults in Storage.swift that this new step
+        //      mutates, so a fresh install ends up in the same state as a migrated user.
+
         // Step 1: Released in v3.0.0 (2025-07-07). Can be removed after 2026-07-07.
         if Storage.shared.migrationStep.value < 1 {
             Storage.shared.migrateStep1()