Follow-up to #25643. Non-blocking — capturing the right-altitude version here rather than a one-off patch.
Background
#25643 (UIScene adoption) made WordPressAppDelegate refresh the remote stores in both willFinishLaunching and the foreground transition that now fires right after the first scene connect, so updateFeatureFlags() / updateRemoteConfig() run twice on every cold launch. (Pre-scene, the legacy applicationWillEnterForeground did not fire on a cold launch, so they ran exactly once.)
A per-call-site debounce in handleWillEnterForeground would fix that one instance. But the deeper gap is that the stores themselves have no client-side fetch throttle, so any caller — or any future caller — that asks twice in quick succession hits the network twice.
Current behavior
RemoteFeatureFlagStore.update(using:then:) enqueues a fetch and calls operationQueue.cancelAllOperations() first — so a second call cancels the first in-flight request and refetches. No "we just fetched, skip" guard.
RemoteConfigStore.update(then:) calls remote.getRemoteConfig directly on every call — no cancellation, no throttle.
Callers are uncoordinated: WordPressAppDelegate (launch + foreground), AccountHelper.updateFeatureFlags() (on account change), and AppUpdateCoordinator (its own RemoteConfigStore). The cold-launch double from #25643 is just the most visible instance.
Proposed
Add a persisted "last fetched" timestamp + a minimum refresh interval to each store, so update() is a no-op (returns the cache) within the interval. Prior art already lives in the codebase: AppUpdateCoordinator.shouldFetchAppStoreInfo gates the App Store lookup with a last-fetched-date + day threshold — apply the same shape to the feature-flag / remote-config stores with a short interval (minutes).
This subsumes the #25643 cold-launch double — no one-off debounce needed in the AppDelegate — and naturally dedupes any future caller.
Caveat: allow an explicit bypass
Feature flags are fetched per-user (the endpoint authenticates to customize flags server-side; see the doc comment on RemoteFeatureFlagStore.update), which is why AccountHelper refetches on account change. A blunt time-only limit would suppress that needed refresh, so the rate limit must support a forced refresh (bypass on auth/account change), or be invalidated when the authenticated user changes.
Files
WordPress/Classes/Stores/RemoteFeatureFlagStore.swift — update(using:then:)
WordPress/Classes/Stores/RemoteConfigStore.swift — update(then:)
- Callers:
WordPress/Classes/System/WordPressAppDelegate.swift (updateFeatureFlags / updateRemoteConfig), WordPress/Classes/Utility/AccountHelper.swift
Surfaced while reviewing #25643; sibling cleanup tracked in #25664.
Follow-up to #25643. Non-blocking — capturing the right-altitude version here rather than a one-off patch.
Background
#25643 (UIScene adoption) made
WordPressAppDelegaterefresh the remote stores in bothwillFinishLaunchingand the foreground transition that now fires right after the first scene connect, soupdateFeatureFlags()/updateRemoteConfig()run twice on every cold launch. (Pre-scene, the legacyapplicationWillEnterForegrounddid not fire on a cold launch, so they ran exactly once.)A per-call-site debounce in
handleWillEnterForegroundwould fix that one instance. But the deeper gap is that the stores themselves have no client-side fetch throttle, so any caller — or any future caller — that asks twice in quick succession hits the network twice.Current behavior
RemoteFeatureFlagStore.update(using:then:)enqueues a fetch and callsoperationQueue.cancelAllOperations()first — so a second call cancels the first in-flight request and refetches. No "we just fetched, skip" guard.RemoteConfigStore.update(then:)callsremote.getRemoteConfigdirectly on every call — no cancellation, no throttle.Callers are uncoordinated:
WordPressAppDelegate(launch + foreground),AccountHelper.updateFeatureFlags()(on account change), andAppUpdateCoordinator(its ownRemoteConfigStore). The cold-launch double from #25643 is just the most visible instance.Proposed
Add a persisted "last fetched" timestamp + a minimum refresh interval to each store, so
update()is a no-op (returns the cache) within the interval. Prior art already lives in the codebase:AppUpdateCoordinator.shouldFetchAppStoreInfogates the App Store lookup with a last-fetched-date + day threshold — apply the same shape to the feature-flag / remote-config stores with a short interval (minutes).This subsumes the #25643 cold-launch double — no one-off debounce needed in the AppDelegate — and naturally dedupes any future caller.
Caveat: allow an explicit bypass
Feature flags are fetched per-user (the endpoint authenticates to customize flags server-side; see the doc comment on
RemoteFeatureFlagStore.update), which is whyAccountHelperrefetches on account change. A blunt time-only limit would suppress that needed refresh, so the rate limit must support a forced refresh (bypass on auth/account change), or be invalidated when the authenticated user changes.Files
WordPress/Classes/Stores/RemoteFeatureFlagStore.swift—update(using:then:)WordPress/Classes/Stores/RemoteConfigStore.swift—update(then:)WordPress/Classes/System/WordPressAppDelegate.swift(updateFeatureFlags/updateRemoteConfig),WordPress/Classes/Utility/AccountHelper.swiftSurfaced while reviewing #25643; sibling cleanup tracked in #25664.