Skip to content

Prewarm workflow assets after offerings refresh#6732

Merged
facumenzella merged 7 commits into
mainfrom
prewarm-workflows
May 6, 2026
Merged

Prewarm workflow assets after offerings refresh#6732
facumenzella merged 7 commits into
mainfrom
prewarm-workflows

Conversation

@facumenzella

@facumenzella facumenzella commented May 5, 2026

Copy link
Copy Markdown
Member

Summary

  • Extends PaywallCacheWarming with warmUpWorkflowCaches(workflow:), which pre-downloads images, videos, and fonts across all workflow screens (in a single parallel task group)
  • Warming is triggered as a side-effect of Purchases.workflow(forOfferingIdentifier:) — no extra network call; assets are warmed the first time a workflow is fetched
  • Gated behind -EnableWorkflowsEndpoint launch argument

Notes

Intentionally prewarming all screens (not just those reachable from initialStepId). If workflows grow to have many screens with complex branching, a bounded graph walk via WorkflowStep.stepTriggerActions would be a better approach — this is documented in a comment in the implementation.

Labels

`pr:feat` `pr:RevenueCatUI` `feat:PaywallsV2`

Test plan

  • Run PaywallsTester with `-EnableWorkflowsEndpoint` and verify workflow assets are prewarmed after the first paywall open (check logs/network for prefetch requests)
  • Verify no regression when flag is absent (no workflow warming fired)
  • Open paywall a second time and verify no duplicate asset downloads

🤖 Generated with Claude Code


Note

Medium Risk
Adds automatic prewarming of workflow images/videos (and fonts on non-tvOS) as a side effect of fetching a workflow, which can increase background work, bandwidth, and caching behavior. Logic is guarded per-workflow ID but still touches asset download and file caching paths concurrently.

Overview
Fetching a workflow via Purchases.workflow(forOfferingIdentifier:) now kicks off background prewarming of that workflow’s screen assets (images, low-res videos, and downloadable fonts on non-tvOS) using the existing PaywallCacheWarming infrastructure.

PaywallCacheWarming gains warmUpWorkflowCaches(workflow:), dedupes by workflow ID, logs a new Strings.paywalls.warming_up_workflow message, and prefetches assets for all screens in parallel via FileRepository/font installation. Cache-warming helpers were extended to compute image/video URLs for WorkflowScreen, and unit test mocks were updated accordingly.

Reviewed by Cursor Bugbot for commit 6e984bb. Bugbot is set up for automated code reviews on this repo. Configure here.

After offerings are fetched, fetch the workflow for the current offering
and pre-download images, videos, and fonts across all workflow screens,
so paywall presentation doesn't block on cold network requests.

Gated behind -EnableWorkflowsEndpoint and respects isAppBackgrounded
for appropriate network priority.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Comment thread Sources/Purchasing/Purchases/Purchases.swift Outdated
Warm workflow assets as a side-effect of fetching the workflow rather
than issuing a redundant second getWorkflow call from warmUpCaches.
This removes the extra background network request after every offerings
refresh and keeps the caching co-located with the only fetch site.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
)
}
if #available(iOS 15.0, macOS 12.0, watchOS 8.0, tvOS 15.0, *),
let cache = self.paywallCache {

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I was wondering about renaming paywallCache to something more meaningful

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yeah at this point it's not paywallCache anymore, for the future!

Images, videos, and fonts are independent — no need to wait for each
group to finish before starting the next.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Comment thread Sources/Paywalls/PaywallCacheWarming.swift Outdated
@facumenzella facumenzella requested a review from vegaro May 5, 2026 15:12
facumenzella and others added 2 commits May 5, 2026 17:17
Follows the same pattern as warmUpPaywallImagesCache, warmUpPaywallVideosCache,
and warmUpPaywallFontsCache.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes and found 1 potential issue.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, have a team admin enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit fdd0930. Configure here.

Comment thread Sources/Paywalls/PaywallCacheWarming.swift Outdated
Comment thread Sources/Paywalls/PaywallCacheWarming.swift Outdated
Comment thread Sources/Paywalls/PaywallCacheWarming.swift Outdated
Comment thread Sources/Paywalls/PaywallCacheWarming.swift Outdated
…ove PaywallComponentsData allocation

- Replace hasLoadedWorkflow bool with warmedWorkflowIDs: Set<String> keyed on
  workflow.id so each distinct workflow is warmed exactly once
- Add guard let self in all task group closures to match existing patterns
- Extract imageURLs(from:localizations:) as a shared free function and add
  allImageURLs/allLowResVideoUrls directly on WorkflowScreen, removing the
  intermediate PaywallComponentsData allocation in warmUpWorkflowCaches

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@emerge-tools

emerge-tools Bot commented May 6, 2026

Copy link
Copy Markdown

📸 Snapshot Test

Base build not found

No build was found for the base commit 79d837d. This is required to generate a snapshot diff for your pull request.

It's possible that you created a branch off the base commit before all of the CI steps have finished processing, e.g. the one that uploads a build to our system. If that's the case, no problem! Just wait and this will eventually resolve.


🛸 Powered by Emerge Tools

@facumenzella facumenzella requested a review from vegaro May 6, 2026 12:37

}

private func imageURLs(

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nice!

)
}
if #available(iOS 15.0, macOS 12.0, watchOS 8.0, tvOS 15.0, *),
let cache = self.paywallCache {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yeah at this point it's not paywallCache anymore, for the future!

@facumenzella facumenzella merged commit 7242565 into main May 6, 2026
14 of 18 checks passed
@facumenzella facumenzella deleted the prewarm-workflows branch May 6, 2026 12:49
matteinn pushed a commit to matteinn/purchases-android that referenced this pull request Jun 5, 2026
### Motivation

When the two-surface workflow slide animation (RevenueCat#3418) starts a
transition, the incoming step's images may not yet be in Coil's cache,
causing a visible pop-in mid-slide. Pre-warming images for every
workflow step before the paywall is shown lets transitions start with
warm caches. We already prewarm offerings, so we should do the same for
workflows.

### Description

Mirrors the iOS approach in
[purchases-ios#6732](RevenueCat/purchases-ios#6732):
after offerings are successfully fetched and cached, the SDK proactively
fetches the workflow for the current offering and pre-downloads all of
its screen images and fonts, the same pattern already used for offering
paywall images via `OfferingImagePreDownloader`.

<!-- CURSOR_SUMMARY -->
---

> [!NOTE]
> **Medium Risk**
> Adds automatic workflow fetch + asset pre-download during offerings
retrieval, increasing background work and network/cache activity which
could impact performance or timing-sensitive flows if misbehaving.
> 
> **Overview**
> Pre-warms workflow step assets to avoid mid-transition pop-in by
**fetching the current offering’s workflow after offerings are loaded**
and pre-downloading its screen images and fonts.
> 
> This introduces `WorkflowAssetPreDownloader` (deduped by workflow id)
and wires it into `WorkflowManager.getWorkflow` (pre-download failures
are logged and do not block returning the workflow). `OfferingsManager`
now accepts an optional `workflowPreWarmer` callback, which
`PurchasesFactory` provides to trigger workflow pre-warming for the
current offering; tests were updated/added to cover these behaviors,
plus a `NoOpLogHandler` for silencing logs in tests.
> 
> <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit
ef23ab0. Bugbot is set up for automated
code reviews on this repo. Configure
[here](https://www.cursor.com/dashboard/bugbot).</sup>
<!-- /CURSOR_SUMMARY -->

---------

Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants