Skip to content

Improve preloading #411

Merged
ianrumac merged 8 commits into
developfrom
ir/fix/upgrade-preloading
May 18, 2026
Merged

Improve preloading #411
ianrumac merged 8 commits into
developfrom
ir/fix/upgrade-preloading

Conversation

@ianrumac
Copy link
Copy Markdown
Collaborator

@ianrumac ianrumac commented May 18, 2026

Changes in this pull request

  • Improves preloading to trigger only for first matched experiment in an experiment group
  • Ensures it re-triggers on changes to the device

Checklist

  • All unit tests pass.
  • All UI tests pass.
  • Demo project builds and runs.
  • I added/updated tests or detailed why my change isn't tested.
  • I added an entry to the CHANGELOG.md for any breaking changes, enhancements, or bug fixes.
  • I have run ktlint in the main directory and fixed any issues.
  • I have updated the SDK documentation as well as the online docs.
  • I have reviewed the contributing guide

Greptile Summary

This PR improves preloading by limiting IF_TRUE campaign rules to the first matching experiment per group (rather than all matches), and adds fingerprint-based re-preload triggers that fire on device/subscription/configuration changes.

  • ConfigLogic.getAllActiveTreatmentPaywallIds is refactored to keep only the first matching IF_TRUE rule per campaign and skip the rest, while ALWAYS and NEVER rules remain unaffected; new tests cover the cross-behavior combinations.
  • PaywallPreload.preloadAllPaywalls gains an AtomicReference-backed fingerprint with CAS dedup + rollback, and ConfigManager.recheckPreloadIfNeeded dispatches PreloadIfEnabled (honoring the device-tier preload-enabled gate) when the fingerprint changes.
  • Superwall registers a ComponentCallbacks2 listener for locale/timezone/interface-style changes and calls recheckPreloadIfNeeded from subscription-status, customer-info, interface-style-override, and review-request change sites; DeviceHelper.preloadFingerprint() bundles all relevant fields into a stable string for comparison.

Confidence Score: 4/5

Safe to merge with awareness of the open fingerprint-rollback-under-concurrent-load scenario and the remaining Superwall.instance path inside customerInfoFlow().

The four core improvements (first-match-only IF_TRUE preloading, AtomicReference CAS dedup with rollback, PreloadIfEnabled dispatch honouring the device-tier gate, and ComponentCallbacks2 re-trigger) all look correct for the non-concurrent steady-state path. The concurrent rollback scenario — where two callers race CAS in sequence and the second rollback restores an unpreloaded fingerprint — remains partially open, as does the customerInfoFlow() path through Superwall.instance in DeviceHelper. Both are carryovers from the prior review cycle and are mitigated but not eliminated by the new CAS logic.

PaywallPreload.kt (CAS rollback under concurrent callers) and DeviceHelper.kt (customerInfoFlow() still routes through Superwall.instance).

Important Files Changed

Filename Overview
superwall/src/main/java/com/superwall/sdk/config/ConfigLogic.kt Refactors getAllActiveTreatmentPaywallIds to short-circuit after the first IF_TRUE match per campaign; minor naming confusion in the filter lambda (rule variable refers to a String ID, not a TriggerRule).
superwall/src/main/java/com/superwall/sdk/config/PaywallPreload.kt Adds AtomicReference lastFingerprint with CAS dedup + rollback to preloadAllPaywalls; the CAS rollback can silently fail under concurrent callers (partially mitigated, noted in prior review thread).
superwall/src/main/java/com/superwall/sdk/config/ConfigManager.kt Adds recheckPreloadIfNeeded which correctly dispatches PreloadIfEnabled (honoring the device-tier gate) when the device fingerprint changes; early-exit guard is a cheap pre-check with authoritative dedup inside preloadAllPaywalls.
superwall/src/main/java/com/superwall/sdk/Superwall.kt Registers ComponentCallbacks2 for device-config changes and wires recheckPreloadIfNeeded to subscription/customer-info/interface-style/review-request change sites; configurationChangeListener is correctly cleaned up in teardown().
superwall/src/main/java/com/superwall/sdk/network/device/DeviceHelper.kt Adds preloadFingerprint() using CustomerInfoFactory and ActiveEntitlementsFactory; activeEntitlements() no longer routes through Superwall.instance, but customerInfoFlow() still does, keeping a partial circular-dependency path alive (noted in prior review thread).
superwall/src/main/java/com/superwall/sdk/config/models/ConfigState.kt PreloadIfEnabled and PreloadAll now both pass fingerprint = deviceHelper.preloadFingerprint() to preloadAllPaywalls; PreloadIfEnabled still correctly guards with computedShouldPreload.
superwall/src/main/java/com/superwall/sdk/dependencies/DependencyContainer.kt Implements ActiveEntitlementsFactory, fixes unsafe .first() crash on empty purchase list, and adds DateSerializer contextual module to paywallJson.
superwall/src/main/java/com/superwall/sdk/models/entitlements/Entitlement.kt Adds explicit @serializable(with = DateSerializer::class) annotations to startsAt, renewedAt, and expiresAt, removing the need to rely on the contextual module for serialization.
superwall/src/test/java/com/superwall/sdk/config/ConfigLogicTest.kt Adds comprehensive tests for the new first-match-only IF_TRUE semantics covering ALWAYS/NEVER interaction, multiple matching IF_TRUE rules, and the six-rule combo case.
superwall/src/test/java/com/superwall/sdk/config/ConfigManagerTest.kt Updates all preloadAllPaywalls mock signatures to include the new fingerprint parameter and adds four recheckPreloadIfNeeded tests covering the no-IF_TRUE, same-fingerprint, changed-fingerprint, and shouldPreload-disabled cases.

Sequence Diagram

sequenceDiagram
    participant SW as Superwall
    participant CM as ConfigManager
    participant PP as PaywallPreload
    participant DH as DeviceHelper
    participant CS as ConfigState.Actions

    Note over SW: Subscription / CustomerInfo / Config / Review change
    SW->>CM: recheckPreloadIfNeeded()
    CM->>CM: getConfig() or return
    CM->>CM: hasIfTrueRule? or return
    CM->>DH: preloadFingerprint()
    DH-->>CM: fingerprint "NEW"
    CM->>PP: lastFingerprint.get() → "OLD" (≠ "NEW")
    CM->>CS: immediate(PreloadIfEnabled)
    CS->>DH: computedShouldPreload(deviceTier)?
    alt preload disabled
        CS-->>CM: return (no-op)
    else preload enabled
        CS->>DH: preloadFingerprint() → "NEW"
        CS->>PP: preloadAllPaywalls(config, ctx, "NEW")
        PP->>PP: CAS lastFingerprint("OLD" → "NEW")
        alt task already running
            PP->>PP: rollback CAS("NEW" → "OLD")
            PP-->>CS: return (retry later)
        else no task running
            PP->>PP: launch preload coroutine
            PP->>PP: getAllActiveTreatmentPaywallIds()
            Note over PP: Only first matching IF_TRUE per campaign
            PP-->>CS: preload started
        end
    end
Loading
Prompt To Fix All With AI
Fix the following 1 code review issue. Work through them one at a time, proposing concise fixes.

---

### Issue 1 of 1
superwall/src/main/java/com/superwall/sdk/config/ConfigLogic.kt:259-261
The lambda parameter `rule` shadows the semantic meaning of "rule" used throughout this file — here it actually holds an experiment ID `String`, not a `TriggerRule`. Renaming it to `experimentId` makes the intent immediately clear and avoids confusion when the surrounding code also uses `rule` for `TriggerRule` instances.

```suggestion
            val skippedIfTrueRules = ifTrueRules.map { it.experiment.id }.filter { experimentId ->
                experimentId != firstIfTrueMatch?.experiment?.id
            }
```

Reviews (3): Last reviewed commit: "Ensure recheck only preloads if enabled" | Re-trigger Greptile

Comment thread superwall/src/main/java/com/superwall/sdk/config/ConfigManager.kt
Comment thread superwall/src/main/java/com/superwall/sdk/config/ConfigManager.kt Outdated
@ianrumac
Copy link
Copy Markdown
Collaborator Author

@greptileai

Comment thread superwall/src/main/java/com/superwall/sdk/config/ConfigManager.kt Outdated
@ianrumac ianrumac force-pushed the ir/fix/upgrade-preloading branch from 0668179 to a00f114 Compare May 18, 2026 15:12
@ianrumac
Copy link
Copy Markdown
Collaborator Author

@greptileai

@ianrumac ianrumac merged commit 14692db into develop May 18, 2026
9 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant