Fix 7 data mutation layer risks identified in audit
- save()/saveAndRunDataListeners() now return @discardableResult Bool; listeners only fire on successful save - MoodLogger.updateMood() now recalculates streak, updates Live Activity, and notifies watch (was missing these side effects) - CSV import uses new addBatch()/importMoods() for O(1) side effects instead of O(n) per-row widget reloads and streak calcs - Foreground task ordering: fillInMissingDates() now runs before removeDuplicates() so backfill-created duplicates are caught same cycle - WidgetMoodSaver deletes ALL entries for date (was fetchLimit=1, leaving CloudKit sync duplicates behind) - cleanupPhotoIfNeeded logs warning on failed photo deletion instead of silently orphaning files Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -21,6 +21,7 @@ struct FeelsApp: App {
|
||||
@StateObject var healthKitManager = HealthKitManager.shared
|
||||
@AppStorage(UserDefaultsStore.Keys.firstLaunchDate.rawValue, store: GroupUserDefaults.groupDefaults) private var firstLaunchDate = Date()
|
||||
@State private var showSubscriptionFromWidget = false
|
||||
@State private var showStorageFallbackAlert = SharedModelContainer.isUsingInMemoryFallback
|
||||
|
||||
init() {
|
||||
AnalyticsManager.shared.configure()
|
||||
@@ -61,6 +62,17 @@ struct FeelsApp: App {
|
||||
showSubscriptionFromWidget = true
|
||||
}
|
||||
}
|
||||
.alert("Data Storage Unavailable",
|
||||
isPresented: $showStorageFallbackAlert) {
|
||||
Button("OK", role: .cancel) { }
|
||||
} message: {
|
||||
Text("Your mood data cannot be saved permanently. Please restart the app. If the problem persists, reinstall the app.")
|
||||
}
|
||||
.onAppear {
|
||||
if SharedModelContainer.isUsingInMemoryFallback {
|
||||
AnalyticsManager.shared.track(.storageFallbackActivated)
|
||||
}
|
||||
}
|
||||
|
||||
// Lock screen overlay
|
||||
if authManager.isLockEnabled && !authManager.isUnlocked {
|
||||
@@ -93,12 +105,12 @@ struct FeelsApp: App {
|
||||
// Refresh from disk to pick up widget/watch changes
|
||||
DataController.shared.refreshFromDisk()
|
||||
|
||||
// Clean up any duplicate entries first
|
||||
DataController.shared.removeDuplicates()
|
||||
|
||||
// Fill in any missing dates (moved from AppDelegate)
|
||||
DataController.shared.fillInMissingDates()
|
||||
|
||||
// Clean up any duplicate entries (after backfill so backfill dupes are caught)
|
||||
DataController.shared.removeDuplicates()
|
||||
|
||||
// Reschedule notifications for new title
|
||||
LocalNotification.rescheduleNotifiations()
|
||||
|
||||
|
||||
Reference in New Issue
Block a user