From a071cafacd41f244f0e35a6372fa0a077aa2f69f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Horv=C3=A1th=20Gergely?= Date: Mon, 9 Feb 2026 18:23:55 +0100 Subject: [PATCH] Add auto-refresh and duplicate-call guard Watch app: add Combine import, scenePhase tracking, a periodic timer and a 10-minute freshness threshold to auto-refresh data when the app becomes active or data becomes stale. Move refresh logic into onChange/onReceive handlers and add shouldAutoRefresh to avoid unnecessary refreshes; removed an immediate proactive token refresh in ContentView. DataStore.refreshAll now early-returns if a refresh is already in progress and logs the skip to prevent duplicate concurrent refreshes. --- .../FirkaWatch Watch App/ContentView.swift | 35 +++++++++++++++++-- .../Services/DataStore.swift | 5 +++ 2 files changed, 38 insertions(+), 2 deletions(-) diff --git a/firka/ios/FirkaWatch Watch App/ContentView.swift b/firka/ios/FirkaWatch Watch App/ContentView.swift index f76a17d..6278fcb 100644 --- a/firka/ios/FirkaWatch Watch App/ContentView.swift +++ b/firka/ios/FirkaWatch Watch App/ContentView.swift @@ -1,10 +1,16 @@ import SwiftUI import WatchConnectivity +internal import Combine struct ContentView: View { var dataStore = DataStore.shared @State private var selectedTab = 0 @State private var isRequestingToken = false + @Environment(\.scenePhase) private var scenePhase + + private let staleCheckTimer = Timer.publish(every: 30, on: .main, in: .common).autoconnect() + + private let autoRefreshThreshold: TimeInterval = 10 * 60 var body: some View { Group { @@ -41,8 +47,6 @@ struct ContentView: View { dataStore.checkTokenState() dataStore.loadFromCache() if dataStore.hasToken { - await dataStore.refreshTokenProactively() - await dataStore.refreshAll() if (dataStore.error == "token_expired" || dataStore.error == "no_token") && !dataStore.recoveryAttempted { @@ -55,6 +59,33 @@ struct ContentView: View { requestToken() } } + .onChange(of: scenePhase) { oldPhase, newPhase in + if newPhase == .active && oldPhase != .active { + if shouldAutoRefresh { + print("[Watch] App came to foreground, data is stale (>10 min), refreshing...") + Task { + await dataStore.refreshAll() + } + } else { + print("[Watch] App came to foreground, data is fresh (<10 min), skipping refresh") + } + } + } + .onReceive(staleCheckTimer) { _ in + if scenePhase == .active && shouldAutoRefresh && !dataStore.isLoading { + print("[Watch] Data became stale (>10 min), auto-refreshing...") + Task { + await dataStore.refreshAll() + } + } + } + } + + private var shouldAutoRefresh: Bool { + guard dataStore.hasToken else { return false } + guard let lastUpdated = dataStore.lastUpdated else { return true } + let elapsed = Date().timeIntervalSince(lastUpdated) + return elapsed >= autoRefreshThreshold } private func requestToken() { diff --git a/firka/ios/FirkaWatch Watch App/Services/DataStore.swift b/firka/ios/FirkaWatch Watch App/Services/DataStore.swift index 56819ea..4a659b5 100644 --- a/firka/ios/FirkaWatch Watch App/Services/DataStore.swift +++ b/firka/ios/FirkaWatch Watch App/Services/DataStore.swift @@ -292,6 +292,11 @@ class DataStore { // MARK: - Data Refresh func refreshAll() async { + guard !isLoading else { + print("[Watch] DataStore.refreshAll() already in progress, skipping duplicate call") + return + } + print("[Watch] DataStore.refreshAll() called") isLoading = true error = nil