feat(workflows): fetch workflows list with offerings and clear it on identity changes#6883
Conversation
4 builds increased size
RevenueCat 1.0 (1)
|
| Item | Install Size Change |
|---|---|
| DYLD.String Table | ⬆️ 6.7 kB |
| RevenueCat.WorkflowManager.WorkflowManager | ⬆️ 1.6 kB |
| RCPaywallViewController.Objc Metadata | ⬇️ -952 B |
| 📝 RCPaywallViewController.viewDidDisappear: | ⬆️ 952 B |
| 📝 RCCustomerCenterViewController.viewDidDisappear: | ⬆️ 832 B |
BinarySizeTest 1.0 (1)
com.revenuecat.binary-size-test.local-source
⚖️ Compare build
📦 Install build
⏱️ Analyze build performance
Total install size change: ⬆️ 18.5 kB (0.15%)
Total download size change: ⬆️ 8.0 kB (0.19%)
Largest size changes
| Item | Install Size Change |
|---|---|
| 📝 RevenueCat.WorkflowManager.prefetchWorkflows(appUserID,isAppBackg... | ⬆️ 2.7 kB |
| 📝 RevenueCat.IdentityManager.init(deviceCache,systemInfo,backend,cu... | ⬆️ 2.4 kB |
| 🗑 RevenueCat.IdentityManager.init(deviceCache,systemInfo,backend,cu... | ⬇️ -2.3 kB |
| 📝 RevenueCat.WorkflowsCache.clearCache | ⬆️ 800 B |
| 📝 RevenueCat.WorkflowsCache.offeringIdToWorkflowId(from) | ⬆️ 688 B |
BinarySizeTest 1.0 (1)
com.revenuecat.binary-size-test.cocoapods
⚖️ Compare build
📦 Install build
⏱️ Analyze build performance
Total install size change: ⬆️ 35.0 kB (0.12%)
Total download size change: ⬆️ 9.2 kB (0.14%)
Largest size changes
| Item | Install Size Change |
|---|---|
| DYLD.String Table | ⬆️ 9.9 kB |
| 📝 RevenueCat.WorkflowManager.prefetchWorkflows(appUserID,isAppBackg... | ⬆️ 2.7 kB |
| 📝 RevenueCat.IdentityManager.init(deviceCache,systemInfo,backend,cu... | ⬆️ 2.4 kB |
| 🗑 RevenueCat.IdentityManager.init(deviceCache,systemInfo,backend,cu... | ⬇️ -2.3 kB |
| 📝 RevenueCat.WorkflowsCache.offeringIdToWorkflowId(from) | ⬆️ 936 B |
BinarySizeTest 1.0 (1)
com.revenuecat.binary-size-test.spm
⚖️ Compare build
📦 Install build
⏱️ Analyze build performance
Total install size change: ⬆️ 18.4 kB (0.17%)
Total download size change: ⬆️ 9.3 kB (0.21%)
Largest size changes
| Item | Install Size Change |
|---|---|
| 📝 RevenueCat.WorkflowManager.prefetchWorkflows(appUserID,isAppBackg... | ⬆️ 2.7 kB |
| 📝 RevenueCat.IdentityManager.init(deviceCache,systemInfo,backend,cu... | ⬆️ 2.4 kB |
| 🗑 RevenueCat.IdentityManager.init(deviceCache,systemInfo,backend,cu... | ⬇️ -2.3 kB |
| 📝 RevenueCat.WorkflowsCache.clearCache | ⬆️ 800 B |
| 📝 RevenueCat.WorkflowsCache.offeringIdToWorkflowId(from) | ⬆️ 688 B |
🛸 Powered by Emerge Tools
bfae745 to
e5d1332
Compare
1a1ec09 to
4b77cec
Compare
e5d1332 to
08dcac9
Compare
4b77cec to
f72350d
Compare
f72350d to
56aa749
Compare
ee563d5 to
1f1480e
Compare
56aa749 to
8dea00c
Compare
8dea00c to
5a4a1bb
Compare
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes and found 1 potential issue.
❌ 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 5a4a1bb. Configure here.
5a4a1bb to
1dcfd49
Compare
1dcfd49 to
ebeaacb
Compare
vegaro
left a comment
There was a problem hiding this comment.
Looks good. I have a question on a particular case
…identity changes Third of three stacked PRs porting purchases-android#3508 to iOS. Wires the workflows pipeline into the offerings + identity lifecycle, behind a gate. - `OfferingsManager` takes an optional `WorkflowManager`. After offerings are fetched from the network and cached, it fetches the workflows list (and prefetches its workflows) before delivering offerings, so `workflowId(forOfferingId:)` is resolvable as soon as offerings succeed. `getWorkflowsList` always calls its completion (even on error), so offerings delivery can never hang. When the manager is nil the offerings path is unchanged. - `IdentityManager` clears the `WorkflowsCache` on login/alias, logout and user switch, alongside the offerings cache, so a user switch never serves the previous user's workflows. - Gating: `SystemInfo.workflowsEndpointEnabled` (driven by the `-EnableWorkflowsEndpoint` launch argument). `configure()` passes the `WorkflowManager` to `OfferingsManager` only when enabled, so the endpoint is off by default and offerings timing is untouched in production. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
ebeaacb to
dec076c
Compare





Port of RevenueCat/purchases-android#3508. Final piece of the workflows port. #6881 (cache) and #6882 (WorkflowManager) are merged, so this targets
maindirectly: it wires the workflows pipeline into the offerings + identity lifecycle, behind a runtime gate.What Changed
OfferingsManagertakes an optionalWorkflowManager. When offerings are delivered fresh (fresh in-memory cache, network success, disk fallback after a backend failure) it fetches the workflows list and prefetches its workflows before delivering, soworkflowId(forOfferingId:)resolves as soon asgetOfferingssucceeds, with no race where a concurrent call delivers before the map is ready. A stale cached read still returns immediately and the workflows list refreshes in the background, so cached delivery is never blocked. A network refresh also callsforceWorkflowsListCacheStale()so the list refreshes alongside offerings.getWorkflowsListalways calls its completion (even on failure), so delivery can never hang. When the manager is nil (gate off), the offerings path is unchanged.IdentityManagerclears theWorkflowsCacheon login/alias, logout, and user switch, so a user switch never serves the previous user's workflows.WorkflowsCache.forceWorkflowsListCacheStale(): marks the list stale so the next fetch refetches, whileworkflowId(forOfferingId:)keeps resolving the current map until then.SystemInfo.workflowsEndpointEnabled(the-EnableWorkflowsEndpointlaunch arg).configure()only wiresWorkflowManager/WorkflowsCacheinto offerings and identity when enabled, so it's off by default and production timing is untouched.Notes
Paywalls: open footer links on Safari on Catalyst #3508), which gates both the cached and network offerings paths (not just the network fetch) and force-stales the list on refresh.trialOrIntroPriceChecker+paywallCacheconstruction moved up inconfigure()(aboveOfferingsManager) so the gatedWorkflowManagercan be passed in. No behavior change, just ordering.Note
Medium Risk
When the launch-arg gate is enabled, fresh offerings completion waits on workflows list fetch/prefetch (bounded by always calling completion on failure); identity and offerings coordination are user-scoped but affect paywall timing.
Overview
Wires the paywall workflows pipeline into offerings and identity, behind
SystemInfo.workflowsEndpointEnabled(-EnableWorkflowsEndpoint). When the gate is off, behavior is unchanged.With the gate on,
OfferingsManageroptionally waits onWorkflowManager.getWorkflowsList(and prefetches) before delivering offerings on fresh in-memory, network, and disk-fallback paths, soworkflowId(forOfferingId:)is ready whengetOfferingssucceeds. Stale in-memory offerings still return immediately; background refreshforceWorkflowsListCacheStale()and refetches the list.IdentityManagerclearsWorkflowsCacheon login, logout, and user switch.Adds
WorkflowsCache.forceWorkflowsListCacheStale(), test mocks, and reordersconfigure()soWorkflowManagercan be injected into offerings (no intended behavior change there).Reviewed by Cursor Bugbot for commit 204fb5b. Bugbot is set up for automated code reviews on this repo. Configure here.