Skip to main content

Displaying Paywalls

Platform specific instructions

iOS

RevenueCat Paywalls will show paywalls in a sheet or fullscreen on iPhone, and there are multiple ways to do this with SwiftUI and UIKit.

  • Depending on an entitlement with presentPaywallIfNeeded
  • Custom logic with presentPaywallIfNeeded
  • Manually with PaywallView or PaywallViewController
import SwiftUI

import RevenueCat
import RevenueCatUI

struct App: View {
var body: some View {
ContentView()
.presentPaywallIfNeeded(
requiredEntitlementIdentifier: "pro",
purchaseCompleted: { customerInfo in
print("Purchase completed: \(customerInfo.entitlements)")
},
restoreCompleted: { customerInfo in
// Paywall will be dismissed automatically if "pro" is now active.
print("Purchases restored: \(customerInfo.entitlements)")
}
)
}
}

Paywalls on iPad

When using presentPaywallIfNeeded to display a paywall on iPad, we'll automatically show a paywall in a modal that is roughly iPhone sized. If instead you prefer to show a paywall that is full screen on iPad, you can use the PaywallView or PaywallViewController methods instead.

Paywalls on iPad

Android

RevenueCat Paywalls will, by default, show paywalls fullscreen and there are multiple ways to do this with Activitys and Jetpack Compose.

  • Depending on an entitlement with PaywallDialog
  • Custom logic with PaywallDialog
  • Manually with Paywall, PaywallDialog, or PaywallActivityLauncher
@OptIn(ExperimentalPreviewRevenueCatUIPurchasesAPI::class)
@Composable
private fun LockedScreen() {
YourContent()

PaywallDialog(
PaywallDialogOptions.Builder()
.setRequiredEntitlementIdentifier(Constants.ENTITLEMENT_ID)
.setListener(
object : PaywallListener {
override fun onPurchaseCompleted(customerInfo: CustomerInfo, storeTransaction: StoreTransaction) {}
override fun onRestoreCompleted(customerInfo: CustomerInfo) {}
}
)
.build()
)
}

React Native

There are several ways to present paywalls:

  • Using RevenueCatUI.presentPaywall: this will display a paywall when invoked.
  • Using RevenueCatUI.presentPaywallIfNeeded: this will present a paywall only if the customer does not have an unlocked entitlement.
  • Manually presenting <RevenueCatUI.Paywall>: this gives you more flexibility on how the paywall is presented.
import RevenueCatUI, { PAYWALL_RESULT } from "react-native-purchases-ui";

async function presentPaywall(): Promise<boolean> {
// Present paywall for current offering:
const paywallResult: PAYWALL_RESULT = await RevenueCatUI.presentPaywall();
// or if you need to present a specific offering:
const paywallResult: PAYWALL_RESULT = await RevenueCatUI.presentPaywall({
offering: offering // Optional Offering object obtained through getOfferings
});

switch (paywallResult) {
case PAYWALL_RESULT.NOT_PRESENTED:
case PAYWALL_RESULT.ERROR:
case PAYWALL_RESULT.CANCELLED:
return false;
case PAYWALL_RESULT.PURCHASED:
case PAYWALL_RESULT.RESTORED:
return true;
default:
return false;
}
}

async function presentPaywallIfNeeded() {
// Present paywall for current offering:
const paywallResult: PAYWALL_RESULT = await RevenueCatUI.presentPaywallIfNeeded({
requiredEntitlementIdentifier: "pro"
});
// If you need to present a specific offering:
const paywallResult: PAYWALL_RESULT = await RevenueCatUI.presentPaywallIfNeeded({
offering: offering, // Optional Offering object obtained through getOfferings
requiredEntitlementIdentifier: "pro"
});
}

There are also several listeners that can be used to handle the paywall lifecycle, such as onPurchaseStarted, onPurchaseCompleted, and onRestoreStarted.

Listeners

When using RevenueCatUI.Paywall, you may use one of the provided listeners to react to user actions.

Available listeners at this time are:

  • onPurchaseStarted
  • onPurchaseCompleted
  • onPurchaseError
  • onPurchaseCancelled
  • onRestoreStarted
  • onRestoreCompleted
  • onRestoreError
  • onDismiss

Flutter

There are several ways to present paywalls:

  • Using RevenueCatUI.presentPaywall: this will display a paywall when invoked.
  • Using RevenueCatUI.presentPaywallIfNeeded: this will present a paywall only if the customer does not have an unlocked entitlement.
  • Manually presenting PaywallView: this gives you more flexibility on how the paywall is presented.
import 'dart:async';
import 'dart:developer';

import 'package:purchases_ui_flutter/purchases_ui_flutter.dart';

void presentPaywall() async {
final paywallResult = await RevenueCatUI.presentPaywall();
log('Paywall result: $paywallResult');
}

void presentPaywallIfNeeded() async {
final paywallResult = await RevenueCatUI.presentPaywallIfNeeded("pro");
log('Paywall result: $paywallResult');
}

Listeners

When using PaywallView, you may use one of the provided listeners to react to user actions. Available listeners at this time are:

  • onPurchaseStarted
  • onPurchaseCompleted
  • onPurchaseError
  • onRestoreCompleted
  • onRestoreError
  • onDismiss

Kotlin Multiplatform

You can present a fullscreen Paywall using the Paywall composable. You have the flexibility to decide when to call this. You could, for instance, add it to your navigation graph.

val options = remember {
PaywallOptions(dismissRequest = { TODO("Handle dismiss") }) {
shouldDisplayDismissButton = true
}
}

Paywall(options)

Listeners

When using Paywall, you may use one of the provided listeners to react to user actions. Available listeners at this time are:

  • onPurchaseStarted
  • onPurchaseCompleted
  • onPurchaseError
  • onPurchaseCancelled
  • onRestoreStarted
  • onRestoreCompleted
  • onRestoreError

Handling paywall navigation

When creating a paywall, consider whether it will be presented in a sheet, or as a full screen view. Sheets won't require a dedicated close button. Full screen views should have either a close button (if presented modally) or a back button (if part of a navigation stack or host) unless you intend to provide a hard paywall to your customers that cannot be bypassed.

Custom fonts

Using custom fonts in your paywall can now be done by uploading font files directly to RevenueCat. See the Custom fonts section for more information.

Including custom fonts in your app

To improve the performance and reduce loading times of your paywall using custom fonts, you can add the font to your app's resources using the instructions below.

Android

To add a custom font to your Android app, place the font file in the res/font folder. Make sure that the filename (without the extension) corresponds to the font name in the paywall editor. See the official Android documentation for more information.

iOS

To add a custom font to your iOS app, go to File and then Add Files to “Your Project Name”. The font file should be a target member of your app, and be registered with iOS by adding the "Fonts provided by the application" key to your Info.plist file. Make sure that the filename (without the extension) corresponds to the font name in the paywall editor. See the official iOS documentation for more information.

Kotlin Multiplatform, React Native, and Flutter

Adding custom fonts to a hybrid app involves adding the font files to the underlying Android and iOS projects following the instructions above.

Changes from legacy Paywalls

Our current Paywalls no longer support footer Paywalls. If your app requests the Paywall for an Offering to display that has a current Paywall, it will display a default version of that paywall instead (see below). Footer mode can still be used on legacy Paywalls templates using the existing method, or the new .originalTemplatePaywallFooter() method on SDK versions that support our current Paywalls.

Close buttons

Our current Paywalls do not require the displayCloseButton parameter (or equivalent for other platforms), and it will have no effect if used, since close buttons can be optionally added directly to your paywall as a component if desired.

Font provider

Our current Paywalls do not support passing in a custom font provider as legacy Paywalls did. Instead, you can now configure Paywalls to use the fonts you've already installed in your app directly from the Dashboard. Using the original handler will have no effect on current Paywalls. For more information, click here

Default Paywall

If you attempt to display a Paywall for an Offering that doesn't have one configured, or that has a Paywall configured which is not supported on the installed SDK version, the RevenueCatUI SDK will display a default Paywall.

The default paywall displays all packages in the Offering.

On iOS it uses the app's accentColor for styling. On Android, it uses the app's Material3's ColorScheme.

📘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.