Refactor lifecycle seams for reward verification locator#3475
Conversation
…t surface. This aligns the core endpoint response mapping with reward payload support and removes unlaunched compatibility layers so callers consume one consistent result model.
This aligns endpoint and response transport naming with the result-based SDK surface while preserving the same backend path and behavior.
This keeps behavior unchanged while reducing return count in reward payload mapping.
Unexpected non-object reward payloads now short-circuit to unsupported reward instead of flowing through type parsing.
46b17a1 to
2d2b0f9
Compare
2d2b0f9 to
bdb926c
Compare
Add purchases lifecycle notifications in core and introduce an AdMob reward verification service locator hook surface, while deferring setup/dispatcher/poller implementation to the stacked follow-up PR.
bdb926c to
94a1b87
Compare
Add focused tests for Purchases lifecycle event dispatching, close-to-event-bus wiring, and reward verification locator hook registration/forwarding behavior.
Add focused lifecycle wiring assertions for both defaults and custom entitlement configure paths, and trim an unnecessary experimental opt-in in the AdMob reward verification service locator.
Switch reward verification lifecycle wiring to instance-owned collaborators and add a small lifecycle listener seam in Purchases so tests can verify behavior without static event-bus mocking.
Deduplicate Purchases lifecycle listener forwarding by using a shared default provider across flavor-specific Purchases entrypoints.
|
@tonidero this is a preliminary PR that implements the service locator in order to do clean ups for reward verifications. Let me know what do you think, hopefully its in the lines of what you were thinking about! |
Replace singleton lifecycle bus usage with an internal instance-based bus, tighten visibility/opt-in usage, and keep listener state transitions coordinated through synchronized helpers.
Avoid invoking lifecycle hook callbacks while holding the ServiceLocator monitor, and add a regression test that verifies reentrant hook mutation does not deadlock while preserving snapshot dispatch semantics.
Prevent stale configured callbacks after close by synchronizing lifecycle bus operations and replace the probabilistic race test with deterministic ordering coverage.
| listenersToNotify.forEach { listener -> | ||
| listener.onPurchasesConfigured(purchases) | ||
| } | ||
| } |
There was a problem hiding this comment.
Event bus invokes listener callbacks inside synchronized block
Medium Severity
PurchasesLifecycleEventBus.onConfigured and onClosed are fully @Synchronized, invoking all listener callbacks while holding the event bus lock. This contradicts the PR's stated "snapshot under synchronization and invoke callbacks outside the lock" pattern applied to RewardVerificationServiceLocator. If any listener's callback blocks on cross-thread work that needs to call register/unregister on the event bus, a deadlock will occur — the exact scenario the locator's own test validates against.
Additional Locations (1)
Reviewed by Cursor Bugbot for commit 10bf442. Configure here.
There was a problem hiding this comment.
If we want both properties (strict ordering + non-locking callbacks), that likely needs a larger redesign rather than local synchronization tweaks. Wondering how big of a concern is this and what are some possible lightweight ways to address it.
Document why lifecycle callbacks are dispatched under the bus lock to preserve ordering guarantees, and remove incorrect unused-parameter suppressions in reward verification lifecycle hooks.
…on-response-surface
…reward-verification-locator-split
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes and found 1 potential issue.
There are 2 total unresolved issues (including 1 from previous review).
❌ 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 ac9f91e. Configure here.
| import com.revenuecat.purchases.PurchasesLifecycleListener | ||
|
|
||
| internal interface RewardVerificationLifecycleHook { | ||
| fun onPurchasesConfigured(purchases: Purchases) |
There was a problem hiding this comment.
Hmm the part that I'm failing to see is how RewardVerificationServiceLocator is actually used/registered... I think this is indeed pretty close to what I meant, but to make it clearer, this is what I thought:
- The
purchasesSDK would add aServiceinterface and a "Purchase.registerService" static method (both marked as @InternalRevenueCatAPI). The Service interface would haveinitializeandclosemethods of some kind, possibly passing in the context, or whatever dependency is needed. I think what we're missing in this PR is theregisterService-like method? In this PR, theServiceis equivalent to thePurchasesLifecycleListener. - The
admobSDK can implement an instance of thisServicewith an initialize/close implementations that would actually perform the initialization/cleanup. We have this part mostly I think. - The
purchasesSDK would callinitialize/closewhere appropriate.
So basically what you have here is pretty close, but I'm just missing the part where this is actually "registered". Lmk if I'm missing something though! Just want to make sure we're on the same page.


Checklist
purchases-iosand hybridsMotivation
Provide the lifecycle infrastructure needed by the AdMob reward verification locator while keeping reward verification behavior implementation out of scope for this PR.
Description
PurchasesLifecycleListenerandPurchasesLifecycleEventBusin:purchasesto publishconfigure/closelifecycle eventsPurchasesLifecycleListenersto centralize default lifecycle forwardingPurchasesvariants to notify lifecycle events on configure/closeRewardVerificationServiceLocatorlifecycle hook registration/dispatch plumbing infeature:admobNote
Medium Risk
Touches core
Purchases.configure/closewiring (defaults + custom entitlement modes), so mistakes could cause missed callbacks or ordering/concurrency issues, though behavior is covered by new unit tests and scoped to internal APIs.Overview
Adds an internal lifecycle notification seam to
purchasesby introducingPurchasesLifecycleListenerplus a synchronizedPurchasesLifecycleEventBusthat broadcastsconfigureandcloseevents (including replaying the latest configured instance to newly-registered listeners).Wires both
Purchases.configure(...)andPurchases.configureInCustomEntitlementsComputationMode(...)to emitonPurchasesConfigured, and bothPurchases.close()implementations to emitonPurchasesClosedbefore delegating to the orchestrator.In
feature:admob, introducesRewardVerificationServiceLocatorthat registers exactly once to the lifecycle listener and fan-outs lifecycle callbacks to a snapshot of registered hooks to avoid deadlocks/reentrancy issues. Adds focused unit tests for the event bus semantics,Purchaseswiring, and locator concurrency/registration behavior.Reviewed by Cursor Bugbot for commit 2b0b876. Bugbot is set up for automated code reviews on this repo. Configure here.