Skip to content
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

PaywallsTester: allow for configuration for demos #3260

Merged
Merged
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
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:
Copy link
Contributor

Choose a reason for hiding this comment

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

Oh nice

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)
)
aboedo marked this conversation as resolved.
Show resolved Hide resolved
}

}
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()
Copy link
Member Author

Choose a reason for hiding this comment

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

updated so that it uses the Purchases.isConfigured method and gets the customer info stream from purchases whenever needed

}
}

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

aboedo marked this conversation as resolved.
Show resolved Hide resolved
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 {
aboedo marked this conversation as resolved.
Show resolved Hide resolved
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
aboedo marked this conversation as resolved.
Show resolved Hide resolved
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
Copy link
Member Author

Choose a reason for hiding this comment

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

added another API key.
Truthfully we should be leveraging our fastlane stuff here, but I'm just going for the fastest possible thing.
This is using Xcode cloud, so we'd need to set that up appropriately as well if we wanted to go the fastlane route

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