Skip to content

[Paywalls V2] Adds template previews#2184

Merged
JayShortway merged 27 commits into
mainfrom
pw2-template-previews
Feb 24, 2025
Merged

[Paywalls V2] Adds template previews#2184
JayShortway merged 27 commits into
mainfrom
pw2-template-previews

Conversation

@JayShortway

@JayShortway JayShortway commented Feb 19, 2025

Copy link
Copy Markdown
Member

This is part 1 of 3:

  1. [This one] Adds the initial offerings_paywalls_v2_templates.json file, and the previews themselves.
  2. [#2192] Adding a Fastlane lane and CircleCI job that updates the offerings_paywalls_v2_templates.json file.
  3. [khepri#12041] Triggering the CircleCI job from the AutoSyncPaywallTemplatesJob in khepri.

Description

This adds previews of all templates, based on the Official Paywalls V2 Templates repo. It includes the offerings network response as JSON, and parses that in a PreviewParameterProvider. It then renders each Paywall as an individual Preview. This way all we have to do is update the JSON file, and we would automatically get updated previews for each paywall. No need to manually add a preview if we add a paywall, for instance.

It doesn't load images and icons yet, but it's already useful to catch various issues such as layouts and gradients.

Design decisions

This section explains why we do things a certain way.

The Composable to preview

I picked LoadedPaywallComponents as the Composable to use for these previews. Other candidates were Paywall and InternalPaywall, but both of these won't work as they use a ViewModel. Android Studio simply refuses to render these.

The source set

The previews, including the giant JSON file, are part of the debug source set, as I didn't want to include this (particularly the JSON) in the published SDK. We might wanna think about doing this for more of our previews. If we want to, we could move the previews to a dedicated previews source set in the future, but for now I thought debug would work fine.

Deserializing JSON into Offerings inside revenuecatui

Our revenuecatui library doesn't handle the deserialization of JSON into Offerings, but that is what we need here. I chose to construct a specific OfferingParser via reflection. This way we don't need to make anything public in purchases just for these previews.

I considered, but ultimately discarded, using the full Purchases SDK. We would configure() and then fetch the Offerings. Doesn't work because configure() uses things like PackageManager which is not available in a preview environment.

Reading the giant JSON file

We are reading the giant JSON file in a very specific way, and the reason for this is the Emerge Snapshots environment. I navigated 2 constraints, that I found out about through trial and error:

  • Reading the entire JSON in memory at once crashes the entire Snapshots run. It lead to 100s of failed (unrelated) snapshots. The environment is memory constrained.
  • Keeping an InputStream open for the entire lifecycle of a PreviewParameterProvider.values Sequence, and yielding items for each item in that Sequence, is not supported. The stream will be closed prematurely in the Emerge environment.

Given these 2 constraints, the only working solution is to reopen the stream every time the values Sequence is asked for a new value. We then parse the file, and only read the the Offering we care about into memory. This is then only kept in memory for as long as the current value is needed. After that, we reopen the stream again and read the next Offering into memory.

How this looks in Android Studio

image

@emerge-tools

emerge-tools Bot commented Feb 19, 2025

Copy link
Copy Markdown

📸 Snapshot Test

4 modified, 18 added, 238 unchanged

Name Added Removed Modified Renamed Unchanged Errored Approval
TestPurchasesUIAndroidCompatibility
com.revenuecat.testpurchasesuiandroidcompatibility
18 0 4 0 238 0 ✅ Approved

🛸 Powered by Emerge Tools

@RevenueCat-Danger-Bot

RevenueCat-Danger-Bot commented Feb 20, 2025

Copy link
Copy Markdown
2 Warnings
⚠️ Size check is being bypassed due to the presence of the label "danger-bypass-size-limit"
⚠️ Size increase: 1535.53 KB

Generated by 🚫 Danger

@codecov

codecov Bot commented Feb 20, 2025

Copy link
Copy Markdown

Codecov Report

Attention: Patch coverage is 0% with 58 lines in your changes missing coverage. Please review.

Project coverage is 80.53%. Comparing base (1f97f4b) to head (a3f38a6).
Report is 1 commits behind head on main.

Files with missing lines Patch % Lines
...evenuecat/purchases/utils/PreviewOfferingParser.kt 0.00% 58 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main    #2184      +/-   ##
==========================================
- Coverage   81.03%   80.53%   -0.50%     
==========================================
  Files         276      277       +1     
  Lines        9397     9455      +58     
  Branches     1333     1334       +1     
==========================================
  Hits         7615     7615              
- Misses       1222     1280      +58     
  Partials      560      560              

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

Comment thread gradle/libs.versions.toml
Comment on lines +43 to +44
emergeGradlePlugin = "4.0.9"
emergeSnapshots = "1.3.2"

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.

This was done on advice from Emerge, but didn't end up fixing the issues.

@JayShortway JayShortway requested review from a team February 21, 2025 17:47
@JayShortway JayShortway marked this pull request as ready for review February 21, 2025 17:47

@tonidero tonidero left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Many thanks for doing this! It's great to have these 💪

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

That's a big json 😅

}

buildFeatures {
buildConfig true

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I guess this might increase build time so very slightly in release without need, but then again, it shouldn't be noticeable at all, I'm totally ok with it

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.

Hmm yea, I researched a bit but I don't think it's possible to only enable this in debug builds. I think it's negligible indeed, especially since there are no buildCofigFields defined for release builds.

dateProvider = { Date() },
)

LoadedPaywallComponents(

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Should we wrap this into a full width/height box with a white background or something like that? To better visualize how it would look like on a device.

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.

Emerge doesn't really support this 🙁 I tried it in 2 ways now:

  • Setting a device spec in the @Preview annotation.
  • Adding a requiredSize modifier to the root.

The device spec is ignored, and the requiredSize results in things being cut off at the top and bottom:

Regarding the use of a parent composable, I'm not sure which alignment to pick then, and I'm afraid whichever alignment we pick might influence our results.

@JayShortway JayShortway Feb 24, 2025

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.

2 more developments on this front:

  • I added a WindowAndDisplayMetrics preview to the debug source set to get some insights into the Emerge Snapshots rendering environment. Turns out it renders at 411 x 683 dp, while Android Studio's default rendering is at 392 x 850 dp, which is quite a bit taller.
  • I tried to use DeviceConfigurationOverride to force the dimensions, but somehow Android Studio fails to render in that case. I think this could be a nice future improvement though.

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