Engineering

How to use RevenueCat Experiments to A/B test in-app paywall design elements 

Test and visualize the impacts of both price and design variables using this workaround.

RevenueCat Experiments
Dan Pannasch

Dan Pannasch

February 28, 2023

We built Experiments to help you easily run, analyze, and draw conclusions from A/B price testing with the full subscription lifecycle in mind.

If you’re using Offerings to configure which products you offer in your app, you’re already set up to use Experiments through RevenueCat. Just set up the two Offerings you want to test against each other, turn on your experiment, and we’ll start splitting customers evenly between those two variants (more details here).

Experiments is loved by our developers for its ability to help visualize your experiments’ impact on lifetime value through a number of key metrics spanning the categories of initial conversion, paid customers, and revenue. 

When you run a price test in RevenueCat, we’ll show you how each metric within the customer journey has changed over time, so you can quickly see which trends are consistent, which ones are volatile, and where to dig deeper.

While Experiments is currently designed to make it easy to test pricing variables, some may want to also measure the impact of other paywall changes to lifetime value. This article will outline a workaround to testing non-pricing elements of your paywall, like design, color, or copy, as well as how to visualize and track those results within the Experiments dashboard. 

Workarounds for testing non-pricing variables

Experiments uses Offerings to represent the hypothesis that’s being tested (the group of products that will be offered to your customers). An Offering is a collection of Packages that contain Products from each store you’re looking to serve that Offering on.

So to run an experiment with RevenueCat, you’ll need to create a new Offering that contains the Products you want to test.

When you run an experiment with RevenueCat, our SDK will call the getOfferings endpoint whenever you want to trigger a paywall to a customer. Based on the experiment variant the customer has been enrolled in, RevenueCat then changes what is provided as the default Offering in the response. 

When an Offering is provided in the response, there are two ways you can use it to control other variables in your paywall experience:

1) Using Offering Metadata

Metadata allows attaching arbitrary information as key/value pairs to your Offering to control how to display your products inside your app. The metadata you configure in an Offering is provided when you fetch an Offering to display from RevenueCat. For example, you could use it to remotely configure elements of your paywall such as the layout, CTA copy, and more. Learn more here

2) Using Offering identifiers

You can also leverage our getOfferings response by designing your app to show a different purchase experience if the default Offering that is returned in RevenueCat equals the Offering you’ve assigned to your experiment. While Offering Metadata would provide you with a UI for remotely controlling specific preset variables, nothing prevents you from doing the same thing in-app by keying off of the Offering identifier itself.

Understanding the Offerings response

Here’s an example of what an Offering response looks like. You can see the first thing that gets attached is the current_offering_id, followed by the list of every other offering identifier that’s available for you.

1{
2   "current_offering_id":"default",
3   "offerings":[
4      {
5         "description":"Standard monthly and annual plans",
6         "identifier":"default",
7         "packages":[
8            {
9               "identifier":"$rc_annual",
10               "platform_product_identifier":"ios_annual_9999"
11            },
12            {
13               "identifier":"$rc_monthly",
14               "platform_product_identifier":"ios_monthly_999"
15            }
16         ]
17      },
18      {
19         "description":"Duplicate offering for paywall testing",
20         "identifier":"experiment_offering",
21         "packages":[
22            {
23               "identifier":"$rc_annual",
24               "platform_product_identifier":"ios_annual_9999"
25            },
26            {
27               "identifier":"$rc_monthly",
28               "platform_product_identifier":"ios_monthly_999"
29            }
30         ]
31      }
32   ]
33}

If you don’t have an experiment running, the current_offering_id will be the Offering you’ve marked as the current Offering in the Dashboard.

When you have an experiment running that current Offering ID will instead show the Offering in the experiment that this subscriber has been enrolled to.

We override the current Offering ID value to show the Offering ID for your experiment instead. The Offering for your experiment will already be part of your list of different Offerings.

If you’ve attached metadata to your Offering, you’ll receive that in the response, and can design your app to programmatically respond to that metadata to modify your paywall remotely.

Or, alternatively, you could key off of that different response so that when the current Offering ID equals that of your experiment Offering, it will trigger other changes to elements of your paywall design.

In that model, you could set up two different Offerings that have the exact same product Offering in them, but tether a non-pricing variable to trigger when one of the two Offerings is displayed. Isolating for that design, copy (or other) element.

1struct RevenueCatView: View {
2    @State private var offering: Offering?
3    
4    var body: some View {
5        VStack {
6            if let offering {
7                if offering.id == "experiment_offering" {
8                    ExperimentPaywall()
9                } else {
10                    Paywall()
11                }
12            } else {
13                ProgressView()
14            }
15        }.task {
16            do  {
17                self.offering = try await Purchases.shared.offerings().current
18            } catch {
19                // Oops
20            }
21        }
22    }
23}

We hope this workaround can enable you to leverage Experiments to test new variables and uncover growth for your app. If you’re looking for other inspiration on what to test with Experiments, check out this blog post on the topic: 10 price test ideas for your subscription app. Happy testing!

In-App Subscriptions Made Easy

See why thousands of the world's tops apps use RevenueCat to power in-app purchases, analyze subscription data, and grow revenue on iOS, Android, and the web.

Related posts

Indies: A Cat's Eye View "How I use RevenueCat to offer users frictionless free trials in my app Personal Best"
Engineering

How I use RevenueCat to offer users frictionless free trials in my app Personal Best

Discover how indie dev Shaun transformed his app's growth by building a ramp, not a wall.

Shaun Donnelly

Shaun Donnelly

July 27, 2023

Trusted Entitlements: Making MiTM attacks a thing of the past
Engineering

Trusted Entitlements: Making MiTM piracy a thing of the past

Safeguarding IAPs and premium features with cryptography and RevenueCat’s latest security feature

Nacho Soto
Toni Rico

Nacho Soto and Toni Rico

July 19, 2023

RevenueCat simplifies Google’s prepaid plans: An opportunity to expand your global subscriber base
Engineering

RevenueCat simplifies Google’s prepaid plans: An opportunity to expand your global subscriber base

Prepaid plans offer users more control over their subscriptions — take advantage with RevenueCat’s ready support for Billing Library 5.

Tina Nguyen

Tina Nguyen

June 29, 2023

Want to see how RevenueCat can help?

RevenueCat enables us to have one single source of truth for subscriptions and revenue data.

Olivier Lemarie, PhotoRoomOlivier Lemarie, PhotoRoom
Read case study