Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 25 additions & 0 deletions code_blocks/tools/paywalls_custom_variables_1.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import androidx.compose.runtime.Composable
import com.revenuecat.purchases.CustomerInfo
import com.revenuecat.purchases.ui.revenuecatui.PaywallDialog
import com.revenuecat.purchases.ui.revenuecatui.PaywallOptions
import com.revenuecat.purchases.ui.revenuecatui.data.processed.CustomVariableValue

@Composable
fun PaywallWithCustomVariables(
customerInfo: CustomerInfo,
onDismiss: () -> Unit
) {
val options = PaywallOptions.Builder { /* dismiss */ }
.setCustomVariables(
mapOf(
"player_name" to CustomVariableValue.String("John"),
"level" to CustomVariableValue.String("42")
)
)
.build()

PaywallDialog(
options = options,
shouldDisplayBlock = { !customerInfo.entitlements.active.containsKey("pro") }
)
}
19 changes: 19 additions & 0 deletions code_blocks/tools/paywalls_custom_variables_1.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import SwiftUI
import RevenueCat
import RevenueCatUI

struct App: View {
@State
var displayPaywall = false

var body: some View {
ContentView()
.sheet(isPresented: self.$displayPaywall) {
PaywallView()
.customPaywallVariables([
"player_name": .string("John"),
"level": .string("42")
])
}
}
}
18 changes: 18 additions & 0 deletions code_blocks/tools/paywalls_custom_variables_2.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import com.revenuecat.purchases.CustomerInfo
import com.revenuecat.purchases.Offering
import com.revenuecat.purchases.ui.revenuecatui.activity.PaywallActivityLauncher
import com.revenuecat.purchases.ui.revenuecatui.data.processed.CustomVariableValue

class MyActivity : ComponentActivity() {
private val paywallActivityLauncher = PaywallActivityLauncher(this)

fun showPaywall(offering: Offering) {
paywallActivityLauncher.launch(
offering = offering,
customVariables = mapOf(
"player_name" to CustomVariableValue.String("John"),
"level" to CustomVariableValue.String("42")
)
)
}
}
15 changes: 15 additions & 0 deletions code_blocks/tools/paywalls_custom_variables_2.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import UIKit
import RevenueCat
import RevenueCatUI

class MyViewController: UIViewController {
func showPaywall() {
let controller = PaywallViewController()

// Set custom variables
controller.setCustomVariable(.string("John"), forKey: "player_name")
Copy link
Member

Choose a reason for hiding this comment

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

The setCustomVariable method takes a plain String, not a CustomVariableValue. This example will not compile.

Option 1 - Using the property (preferred for Swift):

import UIKit
import RevenueCat
import RevenueCatUI

class MyViewController: UIViewController {
    func showPaywall() {
        let controller = PaywallViewController()

        // Set custom variables
        controller.customVariables = [
            "player_name": .string(\"John\"),
            "level": .string(\"42\")
        ]

        self.present(controller, animated: true)
    }
}

Option 2 - Using the ObjC-compatible method (takes plain String):

controller.setCustomVariable(\"John\", forKey: \"player_name\")
controller.setCustomVariable(\"42\", forKey: \"level\")

controller.setCustomVariable(.string("42"), forKey: "level")

self.present(controller, animated: true)
}
}
22 changes: 22 additions & 0 deletions code_blocks/tools/paywalls_custom_variables_flutter.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import 'package:purchases_ui_flutter/purchases_ui_flutter.dart';

@override
Widget build(BuildContext context) {
return Scaffold(
body: SafeArea(
child: Center(
child: PaywallView(
offering: offering, // Optional Offering object obtained through getOfferings
customVariables: {
"player_name": "John",
"level": "42"
},
onDismiss: () {
// Dismiss the paywall, i.e. remove the view, navigate to another screen, etc.
// Will be called when the close button is pressed (if enabled) or when a purchase succeeds.
},
),
),
),
);
}
22 changes: 22 additions & 0 deletions code_blocks/tools/paywalls_custom_variables_rn.ts.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import React from 'react';
import { View } from 'react-native';

import RevenueCatUI from 'react-native-purchases-ui';

return (
<View style={{ flex: 1 }}>
<RevenueCatUI.Paywall
options={{
offering: offering, // Optional Offering object obtained through getOfferings
customVariables: {
player_name: "John",
level: "42"
}
}}
onDismiss={() => {
// Dismiss the paywall, i.e. remove the view, navigate to another screen, etc.
// Will be called when the close button is pressed (if enabled) or when a purchase succeeds.
}}
/>
</View>
);
23 changes: 22 additions & 1 deletion docs/tools/paywalls/creating-paywalls/variables.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,27 @@ We support the following variables:
| product.relative_discount | 19% | 19% | 19% | 19% | 19% | 19% | 19% |
| product.store_product_name | Pro Access | Pro Access | Pro Access | Pro Access | Pro Access | Pro Access | Pro Access |

## Custom variables

In addition to the product variables listed above, you can create **Custom variables** to insert dynamic content that isn't tied to a specific product. This is useful for displaying values like user-specific information, app state, or promotional messaging that changes based on your app's context.

### Creating and using custom variables in the editor

To create a custom variable:

1. In the paywall editor, select the **Variables** tab from the left-hand sidebar
2. Click "Create variable"
3. Set the name of the variable - this will be how you reference it in your paywall
4. Give it a default value - this is what will be displayed in the preview and used as a fallback if no value is provided

The default value you set applies across your entire project, ensuring consistency wherever the variable is used. Once created, you can insert the custom variable into any text field using the same `{{ custom.variable_name }}` syntax.

### Passing custom variable values from your app

When displaying a paywall in your app, you'll need to pass actual values for any custom variables you've created. This allows you to provide dynamic, context-specific content based on the user's session or app state.

For details on how to pass custom variable values when presenting a paywall, see [Displaying Paywalls](/documentation/displaying-paywalls#custom-variables).

## Countdown variables

When using a [Countdown component](/tools/paywalls/creating-paywalls/components#countdown) on your paywall, you can use countdown-specific variables within any text component inside that countdown. These variables display the time remaining until the countdown's target date.
Expand Down Expand Up @@ -95,4 +116,4 @@ These modifiers can be used with the `|` operator after the variable name, for e

| Question | Answer |
| ------------------ | ------------------|
| How is the `product.relative_discount` variable calculated? | `relative_discount` returns the localized discount percentage of a given package compared to the most expensive package per period that's offered on your paywall. For example, if you're displaying a monthly package that is $10/month, and a yearly package that is $80/year (or $6.67/month), the relative discount for the annual package is 67%. Because of this, we recommend only using this variable within packages that are NOT the most expensive package per period, as the most expensive package will have a null value returned, since its relative discount is null. |
| How is the `product.relative_discount` variable calculated? | `relative_discount` returns the localized discount percentage of a given package compared to the most expensive package per period that's offered on your paywall. For example, if you're displaying a monthly package that is $10/month, and a yearly package that is $80/year (or $6.67/month), the relative discount for the annual package is 67%. Because of this, we recommend only using this variable within packages that are NOT the most expensive package per period, as the most expensive package will have a null value returned, since its relative discount is null. |
56 changes: 55 additions & 1 deletion docs/tools/paywalls/displaying-paywalls.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -347,6 +347,60 @@ import preferredLocaleReactNative from '@site/code_blocks/tools/paywalls_preferr
/>


## Custom variables {#custom-variables}

Custom variables allow you to pass dynamic values from your app to display in your paywall text. This is useful for displaying values like user-specific information, app state, or promotional messaging that changes based on your app's context.

When you create custom variables in the [paywall editor](/tools/paywalls/creating-paywalls/variables#custom-variables), you set default values that will be used as fallbacks. When displaying the paywall in your app, you can override these defaults with actual runtime values specific to the user's session.

### iOS

import customVarsSwift from '@site/code_blocks/tools/paywalls_custom_variables_1.swift?raw';
import customVarsUIKit from '@site/code_blocks/tools/paywalls_custom_variables_2.swift?raw';

<RCCodeBlock
tabs={[
{ type: "swift", title: "SwiftUI", content: customVarsSwift },
{ type: "swift", title: "UIKit", content: customVarsUIKit }
]}
/>

### Android

import customVarsKotlin from '@site/code_blocks/tools/paywalls_custom_variables_1.kt?raw';
import customVarsActivity from '@site/code_blocks/tools/paywalls_custom_variables_2.kt?raw';

<RCCodeBlock
tabs={[
{ type: "kotlin", title: "Composable", content: customVarsKotlin },
{ type: "kotlin", title: "Activity", content: customVarsActivity }
]}
/>

### React Native

import customVarsRN from '@site/code_blocks/tools/paywalls_custom_variables_rn.ts.txt?raw';

<RCCodeBlock
tabs={[
{ type: "rn", content: customVarsRN }
]}
/>

### Flutter

import customVarsFlutter from '@site/code_blocks/tools/paywalls_custom_variables_flutter.dart?raw';

<RCCodeBlock
tabs={[
{ type: "flutter", content: customVarsFlutter }
]}
/>

:::info Variable naming
Custom variable keys must start with a letter and can only contain letters, numbers, and underscores. In your paywall text, reference them using the template syntax: `{{ custom.variable_name }}`
:::

## Custom fonts

Using custom fonts in your paywall can now be done by uploading font files directly to RevenueCat. See the [Custom fonts](/tools/paywalls/creating-paywalls/components#custom-fonts) section for more information.
Expand Down Expand Up @@ -451,4 +505,4 @@ On Android, it uses the app's `Material3`'s `ColorScheme`.

:::tip Targeting
If your app supports our legacy Paywall templates, consider using Targeting to create an audience that only receives your new Paywall if they're using an SDK version that does not support our current Paywalls. This will ensure that older app versions continue to receive the Offering and Paywall that they support, while any app versions running a supported RC SDK version receive your new Paywall. [Learn more about Targeting.](/tools/targeting)
:::
:::
Loading