Skip to content

Commit

Permalink
PaywallsTester: allow for configuration for demos (#3260)
Browse files Browse the repository at this point in the history
This introduces buttons that allow you to reconfigure the app with a
different API key.

This will be useful when doing demos for customers, since the Testing
app's Offerings are pretty cluttered at this point.

<img width="447" alt="image"
src="https://github.com/RevenueCat/purchases-ios/assets/3922667/c603f289-4e5e-45fd-b725-a76b552f4fde">
  • Loading branch information
aboedo authored Oct 4, 2023
1 parent 31a4113 commit 87182db
Show file tree
Hide file tree
Showing 4 changed files with 139 additions and 76 deletions.
71 changes: 54 additions & 17 deletions Tests/TestingApps/PaywallsTester/PaywallsTester/Configuration.swift
Original file line number Diff line number Diff line change
Expand Up @@ -6,34 +6,71 @@
//

import Foundation
import RevenueCat

enum Configuration {
final class Configuration: ObservableObject {
static let shared = Configuration()

#warning("Configure API key if you want to test paywalls from your dashboard")

// Note: you can leave this empty to use the production server, or point to your own instance.
static let proxyURL = ""
static let apiKey = ""
@Published var currentMode: Mode {
didSet {
self.configure()
}
}

static let entitlement = "pro"

}

extension Configuration {
enum Mode: Equatable {
case custom, testing, demos, listOnly
}

static var effectiveApiKey: String = {
return Self.apiKey.nonEmpty ?? Self.apiKeyFromCI
}()
#warning("Configure API key if you want to test paywalls from your dashboard")
// Note: you can leave this empty to use the production server, or point to your own instance.
private static let proxyURL = ""
private static let apiKey = ""

// This is modified by CI:
private static let apiKeyFromCI = ""
private static let apiKeyFromCIForTesting = ""
private static let apiKeyFromCIForDemos = ""

}
private init() {
if Self.apiKey.isEmpty {
self.currentMode = Self.apiKeyFromCIForTesting.isEmpty ? .listOnly : .testing
} else {
self.currentMode = .custom
}

Purchases.logLevel = .verbose
Purchases.proxyURL = Self.proxyURL.isEmpty
? nil
: URL(string: Self.proxyURL)!

self.configure()
}

// MARK: - Extensions
var currentAPIKey: String? {
switch currentMode {
case .custom:
Self.apiKey
case .testing:
Self.apiKeyFromCIForTesting
case .demos:
Self.apiKeyFromCIForDemos
case .listOnly:
nil
}
}

private extension String {
private func configure() {
guard let currentAPIKey = self.currentAPIKey,
!currentAPIKey.isEmpty else {
return
}

var nonEmpty: String? { return self.isEmpty ? nil : self }
Purchases.configure(
with: .init(withAPIKey: currentAPIKey)
.with(entitlementVerificationMode: .informational)
.with(usesStoreKit2IfAvailable: true)
)
}

}
23 changes: 1 addition & 22 deletions Tests/TestingApps/PaywallsTester/PaywallsTester/SimpleApp.swift
Original file line number Diff line number Diff line change
Expand Up @@ -5,36 +5,15 @@
// Created by Nacho Soto on 5/30/23.
//

import RevenueCat
import RevenueCatUI
import SwiftUI

@main
struct SimpleApp: App {

init() {
Purchases.logLevel = .verbose
Purchases.proxyURL = Configuration.proxyURL.isEmpty
? nil
: URL(string: Configuration.proxyURL)!

Purchases.configure(
with: .init(withAPIKey: Configuration.effectiveApiKey)
.with(entitlementVerificationMode: .informational)
.with(usesStoreKit2IfAvailable: true)
)
}

var body: some Scene {
WindowGroup {
AppContentView(
customerInfoStream: Self.apiKeyIsConfigured
? Purchases.shared.customerInfoStream
: nil
)
AppContentView()
}
}

private static let apiKeyIsConfigured = !Configuration.effectiveApiKey.isEmpty

}
Original file line number Diff line number Diff line change
Expand Up @@ -11,27 +11,21 @@ import SwiftUI

struct AppContentView: View {

let customerInfoStream: AsyncStream<CustomerInfo>?

init(customerInfoStream: AsyncStream<CustomerInfo>?) {
self.customerInfoStream = customerInfoStream
}

#if DEBUG
init(customerInfo: CustomerInfo) {
self.init(customerInfoStream: .init(unfolding: { customerInfo }))
}
#endif
@ObservedObject
private var configuration = Configuration.shared

@State
private var customerInfo: CustomerInfo?

@State
private var showingDefaultPaywall: Bool = false

@State
private var customerInfoTask: Task<(), Never>? = nil

var body: some View {
TabView {
if self.isPurchasesConfigured {
if Purchases.isConfigured {
NavigationView {
ZStack {
self.background
Expand All @@ -52,7 +46,7 @@ struct AppContentView: View {
}
#endif

if self.isPurchasesConfigured {
if Purchases.isConfigured {
OfferingsList()
.tabItem {
Label("All paywalls", systemImage: "network")
Expand Down Expand Up @@ -97,29 +91,26 @@ struct AppContentView: View {
Spacer()
}
Spacer()
Button("Present default paywall") {

Text("Currently configured for \(self.descriptionForCurrentMode())")
.font(.footnote)

ConfigurationButton(title: "Configure for demos", mode: .demos, configuration: configuration) {
self.configuration.currentMode = .demos
}

ConfigurationButton(title: "Configure for testing", mode: .testing, configuration: configuration) {
self.configuration.currentMode = .testing
}

ProminentButton(title: "Present default paywall") {
showingDefaultPaywall.toggle()
}
.buttonStyle(.plain)
.frame(maxWidth: .infinity, maxHeight: 50)
.font(.headline)
.background(Color.accentColor)
.foregroundColor(.white)
.cornerRadius(8)
}
.padding(.horizontal)
.padding(.bottom, 80)
.frame(maxWidth: .infinity, maxHeight: .infinity)
.navigationTitle("Simple App")
.task {
if let stream = self.customerInfoStream {
for await info in stream {
self.customerInfo = info
self.showingDefaultPaywall = self.showingDefaultPaywall && info.activeSubscriptions.count == 0
}

}
}
#if DEBUG
.overlay {
if #available(iOS 16.0, macOS 13.0, *) {
Expand All @@ -144,10 +135,62 @@ struct AppContentView: View {
#endif
}
}
.task(id: self.configuration.currentMode) {
if Purchases.isConfigured {
for await info in Purchases.shared.customerInfoStream {
self.customerInfo = info
self.showingDefaultPaywall = self.showingDefaultPaywall && info.activeSubscriptions.isEmpty
}
}
}
}

private func descriptionForCurrentMode() -> String {
switch self.configuration.currentMode {
case .custom:
return "the API set locally in Configuration.swift"
case .testing:
return "the Paywalls Tester app in RevenueCat Dashboard"
case .demos:
return "Demos"
case .listOnly:
return "showcasing the different Paywall Templates and Modes available"
}
}

}
private struct ProminentButton: View {
var title: String
var action: () -> Void
var background: Color = .accentColor

var body: some View {
Button(action: self.action) {
Text(self.title)
.bold()
.padding()
.frame(maxWidth: .infinity)
.background(background)
.foregroundColor(.white)
.cornerRadius(10)
}
}
}

private struct ConfigurationButton: View {

private var isPurchasesConfigured: Bool {
return self.customerInfoStream != nil
var title: String
var mode: Configuration.Mode
@ObservedObject var configuration: Configuration
var action: () -> Void

var body: some View {
ProminentButton(
title: self.title,
action: self.action,
background: self.configuration.currentMode == mode ? Color.gray : Color.accentColor
)
.disabled(self.configuration.currentMode == mode)
}

}
Expand All @@ -169,7 +212,7 @@ struct AppContentView_Previews: PreviewProvider {

static var previews: some View {
NavigationStack {
AppContentView(customerInfo: TestData.customerInfo)
AppContentView()
}
}

Expand Down
14 changes: 9 additions & 5 deletions Tests/TestingApps/PaywallsTester/ci_scripts/ci_pre_xcodebuild.sh
Original file line number Diff line number Diff line change
@@ -1,14 +1,18 @@
#!/bin/bash -e

PAYWALLS_TESTER_API_KEY=$REVENUECAT_XCODE_CLOUD_SIMPLE_APP_API_KEY
PAYWALLS_TESTER_API_KEY_FOR_TESTING=$REVENUECAT_XCODE_CLOUD_SIMPLE_APP_API_KEY_FOR_TESTING
PAYWALLS_TESTER_API_KEY_FOR_DEMOS=$REVENUECAT_XCODE_CLOUD_SIMPLE_APP_API_KEY_FOR_DEMOS
SCRIPT_DIR=$( cd -- "$( dirname -- "${BASH_SOURCE[0]}" )" &> /dev/null && pwd )

if [ -z "$PAYWALLS_TESTER_API_KEY" ]; then
echo "PaywallsTester API key environment variable is not set."
if [ -z "$PAYWALLS_TESTER_API_KEY_FOR_TESTING" ]; then
echo "PaywallsTester API key for testing environment variable is not set."
elif [ -z "$PAYWALLS_TESTER_API_KEY_FOR_DEMOS" ]; then
echo "PaywallsTester API key for demos environment variable is not set."
else
echo "Replacing API key on PaywallsTester"
echo "Replacing API keys on PaywallsTester"

file="$SCRIPT_DIR/../PaywallsTester/Configuration.swift"
sed -i.bak 's/private static let apiKeyFromCI = ""/private static let apiKeyFromCI = "'$PAYWALLS_TESTER_API_KEY'"/g' $file
sed -i.bak 's/private static let apiKeyFromCIForTesting = ""/private static let apiKeyFromCIForTesting = "'$PAYWALLS_TESTER_API_KEY_FOR_TESTING'"/g' $file
sed -i.bak 's/private static let apiKeyFromCIForDemos = ""/private static let apiKeyFromCIForDemos = "'$PAYWALLS_TESTER_API_KEY_FOR_DEMOS'"/g' $file
rm $file.bak
fi

0 comments on commit 87182db

Please sign in to comment.