Skip to content

Displaying Paywalls docs that don't suck (WIP) #1001

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 5 commits into
base: main
Choose a base branch
from
Open
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
17 changes: 17 additions & 0 deletions code_blocks/tools/paywalls/automatic_dismissal.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import SwiftUI
import RevenueCat
import RevenueCatUI

struct App: View {
var body: some View {
ContentView()
.presentPaywallIfNeeded(requiredEntitlementIdentifier: "premium") { result in
switch result {
case .restored(let customerInfo):
print("Restored: \(customerInfo)")
default:
break
}
}
}
}
32 changes: 32 additions & 0 deletions code_blocks/tools/paywalls/callbacks_swiftui.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import SwiftUI
import RevenueCat
import RevenueCatUI

struct App: View {
var body: some View {
ContentView()
.presentPaywallIfNeeded(requiredEntitlementIdentifier: "premium") {
purchaseCompleted: { customerInfo in
unlockPremiumFeatures()
},
restoreCompleted: { customerInfo in
updateUIForRestoredPurchases()
},
dismiss: {
handlePaywallDismissal()
}
}
}

private func unlockPremiumFeatures() {
// Handle premium features unlock
}

private func updateUIForRestoredPurchases() {
// Update UI for restored purchases
}

private func handlePaywallDismissal() {
// Handle paywall dismissal
}
}
9 changes: 9 additions & 0 deletions code_blocks/tools/paywalls/embedded_flutter.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import 'package:flutter/material.dart';
import 'package:purchases_ui_flutter/purchases_ui_flutter.dart';

class MyWidget extends StatelessWidget {
@override
Widget build(BuildContext context) {
return PaywallView(offeringIdentifier: "your_offering");
}
}
13 changes: 13 additions & 0 deletions code_blocks/tools/paywalls/embedded_kmp.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import com.revenuecat.purchases.ui.revenuecatui.Paywall
import com.revenuecat.purchases.ui.revenuecatui.PaywallOptions

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

Paywall(options)
}
8 changes: 8 additions & 0 deletions code_blocks/tools/paywalls/embedded_react_native.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import React from 'react';
import RevenueCatUI from 'react-native-purchases-ui';

function MyComponent() {
return (
<RevenueCatUI.Paywall offeringIdentifier="your_offering" />
);
}
8 changes: 8 additions & 0 deletions code_blocks/tools/paywalls/error_handling.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
try {
const result = await RevenueCatUI.presentPaywall();
if (result === 'ERROR') {
Alert.alert("Error loading paywall", "Please check your connection and try again.");
}
} catch (error) {
console.error(error);
}
8 changes: 8 additions & 0 deletions code_blocks/tools/paywalls/fullscreen_android.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import android.content.Context
import com.revenuecat.purchases.ui.revenuecatui.PaywallActivityLauncher

class MainActivity : AppCompatActivity() {
fun presentPaywall(context: Context) {
PaywallActivityLauncher.launch(context, offeringIdentifier = "your_offering")
}
}
19 changes: 19 additions & 0 deletions code_blocks/tools/paywalls/fullscreen_flutter.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import 'package:flutter/material.dart';
import 'package:purchases_ui_flutter/purchases_ui_flutter.dart';

class FullscreenPaywall extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
body: SafeArea(
child: PaywallView(
offering: offering, // Optional Offering object
onDismiss: () {
// Handle fullscreen paywall dismissal
// Navigate away or close the screen
},
),
),
);
}
}
16 changes: 16 additions & 0 deletions code_blocks/tools/paywalls/fullscreen_kmp.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import com.revenuecat.purchases.ui.revenuecatui.Paywall
import com.revenuecat.purchases.ui.revenuecatui.PaywallOptions

@Composable
fun FullscreenPaywall() {
val options = remember {
PaywallOptions(dismissRequest = {
// Handle fullscreen paywall dismissal
// Navigate away or close the screen
}) {
shouldDisplayDismissButton = false // Fullscreen without dismiss button
}
}

Paywall(options)
}
59 changes: 59 additions & 0 deletions code_blocks/tools/paywalls/fullscreen_react_native.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
import React from 'react';
import { View } from 'react-native';
import RevenueCatUI, { PAYWALL_RESULT } from 'react-native-purchases-ui';

// Method 1: Using presentPaywallIfNeeded with fullScreenCover presentation mode
async function presentFullscreenPaywall() {
const paywallResult: PAYWALL_RESULT = await RevenueCatUI.presentPaywallIfNeeded({
requiredEntitlementIdentifier: "premium",
presentationMode: "fullScreenCover"
});

switch (paywallResult) {
case PAYWALL_RESULT.PURCHASED:
case PAYWALL_RESULT.RESTORED:
console.log('Access granted');
break;
case PAYWALL_RESULT.CANCELLED:
console.log('User cancelled');
break;
case PAYWALL_RESULT.ERROR:
console.log('Error occurred');
break;
}
}

// Method 2: Using Paywall component in fullscreen container
function FullscreenPaywallComponent() {
return (
<View style={{ flex: 1 }}>
<RevenueCatUI.Paywall
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>
);
}

// Method 3: Using Paywall component with specific offering
function FullscreenPaywallWithOffering() {
return (
<View style={{ flex: 1 }}>
<RevenueCatUI.Paywall
options={{
offering: offering // Optional Offering object obtained through getOfferings
}}
onRestoreCompleted={({customerInfo}: { customerInfo: CustomerInfo }) => {
// Optional listener. Called when a restore has been completed.
// This may be called even if no entitlements have been granted.
}}
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>
);
}
11 changes: 11 additions & 0 deletions code_blocks/tools/paywalls/fullscreen_swiftui.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import SwiftUI
import RevenueCat
import RevenueCatUI

struct App: View {
var body: some View {
ContentView()
.presentPaywallIfNeeded(requiredEntitlementIdentifier: "premium")
.presentationMode(.fullScreenCover)
}
}
10 changes: 10 additions & 0 deletions code_blocks/tools/paywalls/fullscreen_uikit.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import UIKit
import RevenueCat
import RevenueCatUI

class ViewController: UIViewController {
func presentPaywall() {
let paywallViewController = PaywallViewController(offeringIdentifier: "your_offering")
present(paywallViewController, animated: true)
}
}
19 changes: 19 additions & 0 deletions code_blocks/tools/paywalls/hard_paywall_flutter.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import 'package:flutter/material.dart';
import 'package:purchases_ui_flutter/purchases_ui_flutter.dart';

class HardPaywall extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
body: SafeArea(
child: PaywallView(
offering: offering, // Optional Offering object
onDismiss: () {
// Hard paywall - no dismissal allowed
// Only dismiss on successful purchase or restore
},
),
),
);
}
}
16 changes: 16 additions & 0 deletions code_blocks/tools/paywalls/hard_paywall_kmp.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import com.revenuecat.purchases.ui.revenuecatui.Paywall
import com.revenuecat.purchases.ui.revenuecatui.PaywallOptions

@Composable
fun HardPaywall() {
val options = remember {
PaywallOptions(dismissRequest = {
// Hard paywall - no dismissal allowed
// Only dismiss on successful purchase or restore
}) {
shouldDisplayDismissButton = false // No dismiss button for hard paywall
}
}

Paywall(options)
}
38 changes: 38 additions & 0 deletions code_blocks/tools/paywalls/hard_paywall_react_native.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import React from 'react';
import { View } from 'react-native';
import RevenueCatUI from 'react-native-purchases-ui';

// Method 1: Hard paywall with current offering
function HardPaywall() {
return (
<View style={{ flex: 1 }}>
<RevenueCatUI.Paywall
onDismiss={() => {
// Hard paywall - no dismissal allowed
// Only dismiss on successful purchase or restore
}}
/>
</View>
);
}

// Method 2: Hard paywall with specific offering
function HardPaywallWithOffering() {
return (
<View style={{ flex: 1 }}>
<RevenueCatUI.Paywall
options={{
offering: offering // Optional Offering object obtained through getOfferings
}}
onRestoreCompleted={({customerInfo}: { customerInfo: CustomerInfo }) => {
// Optional listener. Called when a restore has been completed.
// This may be called even if no entitlements have been granted.
}}
onDismiss={() => {
// Hard paywall - no dismissal allowed
// Only dismiss on successful purchase or restore
}}
/>
</View>
);
}
11 changes: 11 additions & 0 deletions code_blocks/tools/paywalls/hard_paywall_swiftui.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import SwiftUI
import RevenueCat
import RevenueCatUI

struct App: View {
var body: some View {
ContentView()
.presentPaywallIfNeeded(requiredEntitlementIdentifier: "premium")
.presentationMode(.fullScreenCover)
}
}
20 changes: 20 additions & 0 deletions code_blocks/tools/paywalls/manual_dismissal.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import SwiftUI
import RevenueCat
import Combine

class PaywallManager: ObservableObject {
private var cancellables = Set<AnyCancellable>()

func setupCustomerInfoListener() {
Purchases.shared.customerInfoStream.sink { customerInfo in
if customerInfo.entitlements["premium"]?.isActive == true {
dismissPaywall()
}
}
.store(in: &cancellables)
}

private func dismissPaywall() {
// Handle paywall dismissal
}
}
17 changes: 17 additions & 0 deletions code_blocks/tools/paywalls/modal_sheet_android.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
@OptIn(ExperimentalPreviewRevenueCatUIPurchasesAPI::class)
@Composable
private fun LockedScreen() {
YourContent()

PaywallDialog(
PaywallDialogOptions.Builder()
.setRequiredEntitlementIdentifier("premium")
.setListener(
object : PaywallListener {
override fun onPurchaseCompleted(customerInfo: CustomerInfo, storeTransaction: StoreTransaction) {}
override fun onRestoreCompleted(customerInfo: CustomerInfo) {}
}
)
.build()
)
}
21 changes: 21 additions & 0 deletions code_blocks/tools/paywalls/modal_sheet_flutter.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import 'dart:async';
import 'dart:developer';
import 'package:purchases_ui_flutter/purchases_ui_flutter.dart';

Future<void> presentModalPaywall() async {
final paywallResult = await RevenueCatUI.presentPaywallIfNeeded("premium");
log('Paywall result: $paywallResult');

switch (paywallResult) {
case PaywallResult.purchased:
case PaywallResult.restored:
print('Access granted');
break;
case PaywallResult.cancelled:
print('User cancelled');
break;
case PaywallResult.error:
print('Error occurred');
break;
}
}
Loading