Narrow consumer R8 rules#3557
Draft
tonidero wants to merge 9 commits into
Draft
Conversation
The consumer rules kept the entire SDK via `-keep class com.revenuecat.** { *; }`,
forcing every consuming app's R8 to retain all classes and members and blocking
shrinking/obfuscation of dead code and internals.
Replace it with conservative, targeted keeps: stable public API names
(`-keepnames`), all `@Serializable` models (kotlinx + custom KSerializers), enum
constant names (the EnumDeserializerWithDefault reflective name match fails
silently), the Galaxy reflective constructor, and the PaywallKt existence probe.
Because R8 only runs in a consuming app, the Maestro e2e job now builds and
installs the minified release APK so the purchase flow exercises these rules
end-to-end.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
tonidero
force-pushed
the
refine-consumer-proguard-rules
branch
from
June 8, 2026 14:00
b7e3392 to
a5a60e9
Compare
The Maestro e2e job now runs against a minified release build, which is non-debuggable and trips the guard in PurchasesFactory that blocks Test Store keys in release builds (it shows SimulatedStoreErrorDialogActivity, crashing the app on launch). uiPreviewMode bypasses the guard but disables billing, so it can't drive the real purchase flow. Add an internal opt-in, DangerousSettings.forTestStoreInReleaseBuild() (@InternalRevenueCatAPI, mirroring forPreviewMode()), that the e2etests app sets so it can use the Test Store in a true release build. Extend the guard to honor it. For RevenueCat-internal testing only. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
tonidero
force-pushed
the
refine-consumer-proguard-rules
branch
from
June 8, 2026 15:01
b4f50aa to
24ee300
Compare
JayShortway
reviewed
Jun 8, 2026
JayShortway
left a comment
Member
There was a problem hiding this comment.
Almost can't believe it 😭
Codecov Report❌ Patch coverage is Additional details and impacted files@@ Coverage Diff @@
## main #3557 +/- ##
==========================================
+ Coverage 80.26% 80.29% +0.03%
==========================================
Files 378 378
Lines 15448 15477 +29
Branches 2143 2145 +2
==========================================
+ Hits 12400 12428 +28
- Misses 2189 2190 +1
Partials 859 859 ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
tonidero
commented
Jun 9, 2026
| } | ||
|
|
||
| internal object WorkflowTriggerTypeDeserializer : EnumDeserializerWithDefault<WorkflowTriggerType>( | ||
| serialName = "com.revenuecat.purchases.common.workflows.WorkflowTriggerType", |
Contributor
Author
There was a problem hiding this comment.
So basically we need each deserializer to have a different name, and after minifiying, the previous strategy didn't work so I'm now passing the FQN as a parameter... In theory, any different string could work, but with the FQN we make sure to avoid duplicates.
| */ | ||
| @InternalRevenueCatAPI | ||
| @JvmStatic | ||
| public fun forTestStoreInReleaseBuild(): DangerousSettings = DangerousSettings( |
Contributor
Author
There was a problem hiding this comment.
I do wonder if we would want to end up making this public... But for now, kept it internal
tonidero
added a commit
to RevenueCat/purchases-unity
that referenced
this pull request
Jun 11, 2026
We're working to narrow down our consumer rules in RevenueCat/purchases-android#3557. However, this makes it not find our code anymore in Unity when minifying since nothing is directly referencing the Java types from Unity. This adds some Keep annotations that from my tests avoid the issue. This does mean we will need to remember to mark all java types as `@Keep` but it was easier than trying to add proguard rules to be used by our customers when importing our plugins. Note this won't do anything until the purchases-android changes actually get merged
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Why
The SDK's consumer Proguard rules kept the entire SDK:
This means that all the code of the SDK is kept and included in our customers apps, which is not great. This PR attempts to reduce what we keep to a few strategic locations that might be more dangerous. It's possible that this can be reduced further, but we are taking a conservative approach for now.
What changes
Replace the blanket keep with targeted rules covering only what R8 can't follow:
EnumDeserializerWithDefaultmatches JSON against enum constant names read reflectively and silently falls back to a default on mismatch, so an obfuscated name would be a silent wrong-value bug, not a crash.feature/galaxy) —GalaxyBillingWrapperis instantiated viaClass.forName+getDeclaredConstructor.PaywallKtexistence probe (ui/revenuecatui) —canUsePaywallUIchecks for the class viaClass.forName.Additionally, for
EnumDeserializerWithDefault, we had to manually pass an individual name for each serializer.Validation
R8 only runs in a consuming app, so the Maestro e2e job now builds and installs the minified release APK (
test-apps/e2etests, debug-signed to avoid release secrets) — the purchase → entitlement-verify flow exercises these rules end-to-end.In order to use release in maestro builds, we had to add an option to bypass the test store release mode check. Currently behind an internal API.