From 6dd755234abf1ee01bd58b25922729116ed843a1 Mon Sep 17 00:00:00 2001 From: alexandrev-tibco Date: Tue, 1 Sep 2026 16:19:52 +0200 Subject: [PATCH] Fix widget: refrescar timeline al guardar Quick Update y al pasar a background (build 86) #42 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - QuickUpdateView.saveAll hacía context.save() directo sin pasar por SnapshotRepository → nunca llamaba a refreshWidgetData() y el widget quedaba obsoleto hasta reabrir la app - Red de seguridad: refreshWidgetData() también en scenePhase .background para cualquier otro camino de guardado directo Co-Authored-By: Claude Fable 5 Claude-Session: https://claude.ai/code/session_01VXtwQP6nYnbLFVqsHiTK5G --- PortfolioJournal.xcodeproj/project.pbxproj | 40 +++++++++---------- .../App/PortfolioJournalApp.swift | 6 +++ .../Views/Dashboard/QuickUpdateView.swift | 3 ++ 3 files changed, 29 insertions(+), 20 deletions(-) diff --git a/PortfolioJournal.xcodeproj/project.pbxproj b/PortfolioJournal.xcodeproj/project.pbxproj index 24d4d6d..40428d3 100644 --- a/PortfolioJournal.xcodeproj/project.pbxproj +++ b/PortfolioJournal.xcodeproj/project.pbxproj @@ -541,7 +541,7 @@ CODE_SIGN_ENTITLEMENTS = PortfolioJournal/PortfolioJournalDebug.entitlements; ENABLE_USER_SCRIPT_SANDBOXING = NO; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 85; + CURRENT_PROJECT_VERSION = 86; DEVELOPMENT_ASSET_PATHS = PortfolioJournal/Assets.xcassets; DEVELOPMENT_TEAM = 2825Q76T7H; ENABLE_PREVIEWS = YES; @@ -559,7 +559,7 @@ "$(inherited)", "@executable_path/Frameworks", ); - MARKETING_VERSION = 1.6.2; + MARKETING_VERSION = 1.6.3; PRODUCT_BUNDLE_IDENTIFIER = com.alexandrevazquez.PortfolioJournal; PRODUCT_NAME = "$(TARGET_NAME)"; STRING_CATALOG_GENERATE_SYMBOLS = YES; @@ -582,7 +582,7 @@ CODE_SIGN_IDENTITY = "Apple Distribution"; CODE_SIGN_STYLE = Manual; PROVISIONING_PROFILE_SPECIFIER = "porfoliojournal"; - CURRENT_PROJECT_VERSION = 85; + CURRENT_PROJECT_VERSION = 86; DEVELOPMENT_ASSET_PATHS = PortfolioJournal/Assets.xcassets; DEVELOPMENT_TEAM = 2825Q76T7H; ENABLE_PREVIEWS = YES; @@ -600,7 +600,7 @@ "$(inherited)", "@executable_path/Frameworks", ); - MARKETING_VERSION = 1.6.2; + MARKETING_VERSION = 1.6.3; PRODUCT_BUNDLE_IDENTIFIER = com.alexandrevazquez.PortfolioJournal; PRODUCT_NAME = "$(TARGET_NAME)"; STRING_CATALOG_GENERATE_SYMBOLS = YES; @@ -739,7 +739,7 @@ ASSETCATALOG_COMPILER_WIDGET_BACKGROUND_COLOR_NAME = WidgetBackground; CODE_SIGN_ENTITLEMENTS = PortfolioJournalWidgetExtension.entitlements; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 85; + CURRENT_PROJECT_VERSION = 86; DEVELOPMENT_ASSET_PATHS = PortfolioJournalWidget/Assets.xcassets; DEVELOPMENT_TEAM = 2825Q76T7H; GENERATE_INFOPLIST_FILE = NO; @@ -752,7 +752,7 @@ "@executable_path/Frameworks", "@executable_path/../../Frameworks", ); - MARKETING_VERSION = 1.6.2; + MARKETING_VERSION = 1.6.3; PRODUCT_BUNDLE_IDENTIFIER = com.alexandrevazquez.PortfolioJournal.PortfolioJournalWidget; PRODUCT_NAME = "$(TARGET_NAME)"; SKIP_INSTALL = YES; @@ -774,7 +774,7 @@ CODE_SIGN_IDENTITY = "Apple Distribution"; CODE_SIGN_STYLE = Manual; PROVISIONING_PROFILE_SPECIFIER = "Portfolio Journalwidget"; - CURRENT_PROJECT_VERSION = 85; + CURRENT_PROJECT_VERSION = 86; DEVELOPMENT_ASSET_PATHS = PortfolioJournalWidget/Assets.xcassets; DEVELOPMENT_TEAM = 2825Q76T7H; GENERATE_INFOPLIST_FILE = NO; @@ -787,7 +787,7 @@ "@executable_path/Frameworks", "@executable_path/../../Frameworks", ); - MARKETING_VERSION = 1.6.2; + MARKETING_VERSION = 1.6.3; PRODUCT_BUNDLE_IDENTIFIER = com.alexandrevazquez.PortfolioJournal.PortfolioJournalWidget; PRODUCT_NAME = "$(TARGET_NAME)"; SKIP_INSTALL = YES; @@ -805,7 +805,7 @@ buildSettings = { BUNDLE_LOADER = "$(TEST_HOST)"; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 85; + CURRENT_PROJECT_VERSION = 86; DEVELOPMENT_TEAM = 2825Q76T7H; GENERATE_INFOPLIST_FILE = YES; IPHONEOS_DEPLOYMENT_TARGET = 17.6; @@ -814,7 +814,7 @@ "@executable_path/Frameworks", "@loader_path/Frameworks", ); - MARKETING_VERSION = 1.6.2; + MARKETING_VERSION = 1.6.3; PRODUCT_BUNDLE_IDENTIFIER = com.alexandrevazquez.PortfolioJournalTests; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_EMIT_LOC_STRINGS = NO; @@ -829,7 +829,7 @@ buildSettings = { BUNDLE_LOADER = "$(TEST_HOST)"; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 85; + CURRENT_PROJECT_VERSION = 86; DEVELOPMENT_TEAM = 2825Q76T7H; GENERATE_INFOPLIST_FILE = YES; IPHONEOS_DEPLOYMENT_TARGET = 17.6; @@ -838,7 +838,7 @@ "@executable_path/Frameworks", "@loader_path/Frameworks", ); - MARKETING_VERSION = 1.6.2; + MARKETING_VERSION = 1.6.3; PRODUCT_BUNDLE_IDENTIFIER = com.alexandrevazquez.PortfolioJournalTests; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_EMIT_LOC_STRINGS = NO; @@ -852,7 +852,7 @@ isa = XCBuildConfiguration; buildSettings = { CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 85; + CURRENT_PROJECT_VERSION = 86; DEVELOPMENT_TEAM = 2825Q76T7H; GENERATE_INFOPLIST_FILE = YES; IPHONEOS_DEPLOYMENT_TARGET = 17.6; @@ -861,7 +861,7 @@ "@executable_path/Frameworks", "@loader_path/Frameworks", ); - MARKETING_VERSION = 1.6.2; + MARKETING_VERSION = 1.6.3; PRODUCT_BUNDLE_IDENTIFIER = com.alexandrevazquez.PortfolioJournalUITests; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_EMIT_LOC_STRINGS = NO; @@ -875,7 +875,7 @@ isa = XCBuildConfiguration; buildSettings = { CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 85; + CURRENT_PROJECT_VERSION = 86; DEVELOPMENT_TEAM = 2825Q76T7H; GENERATE_INFOPLIST_FILE = YES; IPHONEOS_DEPLOYMENT_TARGET = 17.6; @@ -884,7 +884,7 @@ "@executable_path/Frameworks", "@loader_path/Frameworks", ); - MARKETING_VERSION = 1.6.2; + MARKETING_VERSION = 1.6.3; PRODUCT_BUNDLE_IDENTIFIER = com.alexandrevazquez.PortfolioJournalUITests; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_EMIT_LOC_STRINGS = NO; @@ -899,7 +899,7 @@ buildSettings = { CODE_SIGN_ENTITLEMENTS = PortfolioJournalQuickUpdateExtension.entitlements; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 85; + CURRENT_PROJECT_VERSION = 86; DEVELOPMENT_TEAM = 2825Q76T7H; GENERATE_INFOPLIST_FILE = NO; INFOPLIST_FILE = PortfolioJournalQuickUpdate/Info.plist; @@ -911,7 +911,7 @@ "@executable_path/Frameworks", "@executable_path/../../Frameworks", ); - MARKETING_VERSION = 1.6.2; + MARKETING_VERSION = 1.6.3; PRODUCT_BUNDLE_IDENTIFIER = com.alexandrevazquez.PortfolioJournal.QuickUpdate; PRODUCT_NAME = "$(TARGET_NAME)"; SKIP_INSTALL = YES; @@ -927,7 +927,7 @@ CODE_SIGN_ENTITLEMENTS = PortfolioJournalQuickUpdateExtension.entitlements; CODE_SIGN_IDENTITY = "Apple Distribution"; CODE_SIGN_STYLE = Manual; - CURRENT_PROJECT_VERSION = 85; + CURRENT_PROJECT_VERSION = 86; DEVELOPMENT_TEAM = 2825Q76T7H; PROVISIONING_PROFILE_SPECIFIER = "PortfolioJournal QuickUpdate AppStore"; GENERATE_INFOPLIST_FILE = NO; @@ -940,7 +940,7 @@ "@executable_path/Frameworks", "@executable_path/../../Frameworks", ); - MARKETING_VERSION = 1.6.2; + MARKETING_VERSION = 1.6.3; PRODUCT_BUNDLE_IDENTIFIER = com.alexandrevazquez.PortfolioJournal.QuickUpdate; PRODUCT_NAME = "$(TARGET_NAME)"; SKIP_INSTALL = YES; diff --git a/PortfolioJournal/App/PortfolioJournalApp.swift b/PortfolioJournal/App/PortfolioJournalApp.swift index 3bedd07..e374d55 100644 --- a/PortfolioJournal/App/PortfolioJournalApp.swift +++ b/PortfolioJournal/App/PortfolioJournalApp.swift @@ -96,6 +96,12 @@ struct PortfolioJournalApp: App { .environmentObject(balancePrivacy) } .onChange(of: scenePhase) { _, newPhase in + if newPhase == .background { + // Safety net for any save path that doesn't refresh the widget + // itself: guarantee the widget is fresh whenever the app leaves + // the foreground (WAL checkpoint + timeline reload). + coreDataStack.refreshWidgetData() + } if newPhase == .active { coreDataStack.refreshWidgetData() // Re-read all Core Data objects from the persistent store so that diff --git a/PortfolioJournal/Views/Dashboard/QuickUpdateView.swift b/PortfolioJournal/Views/Dashboard/QuickUpdateView.swift index 1a78378..8af73da 100644 --- a/PortfolioJournal/Views/Dashboard/QuickUpdateView.swift +++ b/PortfolioJournal/Views/Dashboard/QuickUpdateView.swift @@ -377,6 +377,9 @@ struct QuickUpdateView: View { } // Keep the share-extension mirror in sync with what was just saved SharedQuickUpdateSync.refreshMirror() + // Direct context.save() bypasses SnapshotRepository, so refresh the + // widget explicitly — otherwise it stays stale until the next app open. + CoreDataStack.shared.refreshWidgetData() // Positive moment: check-in saved. Ask for a rating once the sheet // is gone (gated to ≥2 months of history, once per version). if ReviewRequestService.shouldRequestAfterCheckIn() {