From 0d3f5775b0b93708e499ff59643c6c35400591cd Mon Sep 17 00:00:00 2001 From: Andy Boedo Date: Thu, 28 Sep 2023 17:40:14 -0300 Subject: [PATCH 01/19] added ugly initial version that works. --- .../PaywallsTester/Configuration.swift | 3 +- .../PaywallsTester/SimpleApp.swift | 6 +- .../PaywallsTester/Views/AppContentView.swift | 75 +++++++++++-------- 3 files changed, 48 insertions(+), 36 deletions(-) diff --git a/Tests/TestingApps/PaywallsTester/PaywallsTester/Configuration.swift b/Tests/TestingApps/PaywallsTester/PaywallsTester/Configuration.swift index 8d8fafb8c6..1e8d76b8da 100644 --- a/Tests/TestingApps/PaywallsTester/PaywallsTester/Configuration.swift +++ b/Tests/TestingApps/PaywallsTester/PaywallsTester/Configuration.swift @@ -26,7 +26,8 @@ extension Configuration { }() // This is modified by CI: - private static let apiKeyFromCI = "" + static let apiKeyFromCI = "" + static let apiKeyForDemos = "" } diff --git a/Tests/TestingApps/PaywallsTester/PaywallsTester/SimpleApp.swift b/Tests/TestingApps/PaywallsTester/PaywallsTester/SimpleApp.swift index 564fbd11fe..99405040ee 100644 --- a/Tests/TestingApps/PaywallsTester/PaywallsTester/SimpleApp.swift +++ b/Tests/TestingApps/PaywallsTester/PaywallsTester/SimpleApp.swift @@ -27,11 +27,7 @@ struct SimpleApp: App { var body: some Scene { WindowGroup { - AppContentView( - customerInfoStream: Self.apiKeyIsConfigured - ? Purchases.shared.customerInfoStream - : nil - ) + AppContentView() } } diff --git a/Tests/TestingApps/PaywallsTester/PaywallsTester/Views/AppContentView.swift b/Tests/TestingApps/PaywallsTester/PaywallsTester/Views/AppContentView.swift index 5229b4cd2f..1043f35f12 100644 --- a/Tests/TestingApps/PaywallsTester/PaywallsTester/Views/AppContentView.swift +++ b/Tests/TestingApps/PaywallsTester/PaywallsTester/Views/AppContentView.swift @@ -11,18 +11,6 @@ import SwiftUI struct AppContentView: View { - let customerInfoStream: AsyncStream? - - init(customerInfoStream: AsyncStream?) { - self.customerInfoStream = customerInfoStream - } - - #if DEBUG - init(customerInfo: CustomerInfo) { - self.init(customerInfoStream: .init(unfolding: { customerInfo })) - } - #endif - @State private var customerInfo: CustomerInfo? @@ -31,7 +19,7 @@ struct AppContentView: View { var body: some View { TabView { - if self.isPurchasesConfigured { + if Purchases.isConfigured { NavigationView { ZStack { self.background @@ -52,7 +40,7 @@ struct AppContentView: View { } #endif - if self.isPurchasesConfigured { + if Purchases.isConfigured { OfferingsList() .tabItem { Label("All paywalls", systemImage: "network") @@ -97,28 +85,30 @@ struct AppContentView: View { Spacer() } Spacer() + + Button("Reconfigure for demos") { + Purchases.configure(withAPIKey: Configuration.apiKeyForDemos) + self.observeCustomerInfoStream() + } + .prominentButtonStyle() + + Button("Reconfigure for testing") { + Purchases.configure(withAPIKey: Configuration.apiKeyFromCI) + self.observeCustomerInfoStream() + } + .prominentButtonStyle() + Button("Present default paywall") { showingDefaultPaywall.toggle() } - .buttonStyle(.plain) - .frame(maxWidth: .infinity, maxHeight: 50) - .font(.headline) - .background(Color.accentColor) - .foregroundColor(.white) - .cornerRadius(8) + .prominentButtonStyle() } .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 - } - - } + self.observeCustomerInfoStream() } #if DEBUG .overlay { @@ -146,12 +136,37 @@ struct AppContentView: View { } } - private var isPurchasesConfigured: Bool { - return self.customerInfoStream != nil + private func observeCustomerInfoStream() { + Task { + if Purchases.isConfigured { + for await info in Purchases.shared.customerInfoStream { + self.customerInfo = info + self.showingDefaultPaywall = self.showingDefaultPaywall && info.activeSubscriptions.count == 0 + } + } + } } } +private extension View { + func prominentButtonStyle() -> some View { + self.modifier(ProminentButtonStyle()) + } +} + +private struct ProminentButtonStyle: ViewModifier { + func body(content: Content) -> some View { + content + .buttonStyle(.plain) + .frame(maxWidth: .infinity, maxHeight: 50) + .font(.headline) + .background(Color.accentColor) + .foregroundColor(.white) + .cornerRadius(8) + } +} + extension CustomerInfo { var hasPro: Bool { @@ -169,7 +184,7 @@ struct AppContentView_Previews: PreviewProvider { static var previews: some View { NavigationStack { - AppContentView(customerInfo: TestData.customerInfo) + AppContentView() } } From 8551647b910eaa738c4e743185a00d42f82bad47 Mon Sep 17 00:00:00 2001 From: Andy Boedo Date: Thu, 28 Sep 2023 17:44:21 -0300 Subject: [PATCH 02/19] update env variable names --- .../PaywallsTester/Configuration.swift | 4 ++-- .../ci_scripts/ci_pre_xcodebuild.sh | 15 ++++++++++----- 2 files changed, 12 insertions(+), 7 deletions(-) diff --git a/Tests/TestingApps/PaywallsTester/PaywallsTester/Configuration.swift b/Tests/TestingApps/PaywallsTester/PaywallsTester/Configuration.swift index 1e8d76b8da..e696163f70 100644 --- a/Tests/TestingApps/PaywallsTester/PaywallsTester/Configuration.swift +++ b/Tests/TestingApps/PaywallsTester/PaywallsTester/Configuration.swift @@ -26,8 +26,8 @@ extension Configuration { }() // This is modified by CI: - static let apiKeyFromCI = "" - static let apiKeyForDemos = "" + static let apiKeyFromCIForTesting = "" + static let apiKeyFromCIForDemos = "" } diff --git a/Tests/TestingApps/PaywallsTester/ci_scripts/ci_pre_xcodebuild.sh b/Tests/TestingApps/PaywallsTester/ci_scripts/ci_pre_xcodebuild.sh index 5a44b634d4..f42e3fd5aa 100755 --- a/Tests/TestingApps/PaywallsTester/ci_scripts/ci_pre_xcodebuild.sh +++ b/Tests/TestingApps/PaywallsTester/ci_scripts/ci_pre_xcodebuild.sh @@ -1,14 +1,19 @@ #!/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 +if [ -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/static let apiKeyFromCIForTesting = ""/static let apiKeyFromCIForTesting = "'$PAYWALLS_TESTER_API_KEY_FOR_TESTING'"/g' $file + sed -i.bak 's/static let apiKeyFromCIForDemos = ""/static let apiKeyFromCIForDemos = "'$PAYWALLS_TESTER_API_KEY_FOR_DEMOS'"/g' $file rm $file.bak fi From c1a905bba78d211b73289b4ef63b6429a63d9889 Mon Sep 17 00:00:00 2001 From: Andy Boedo Date: Thu, 28 Sep 2023 17:45:24 -0300 Subject: [PATCH 03/19] fixes from previous commit --- .../PaywallsTester/PaywallsTester/Configuration.swift | 2 +- .../PaywallsTester/Views/AppContentView.swift | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/Tests/TestingApps/PaywallsTester/PaywallsTester/Configuration.swift b/Tests/TestingApps/PaywallsTester/PaywallsTester/Configuration.swift index e696163f70..439b216b7a 100644 --- a/Tests/TestingApps/PaywallsTester/PaywallsTester/Configuration.swift +++ b/Tests/TestingApps/PaywallsTester/PaywallsTester/Configuration.swift @@ -22,7 +22,7 @@ enum Configuration { extension Configuration { static var effectiveApiKey: String = { - return Self.apiKey.nonEmpty ?? Self.apiKeyFromCI + return Self.apiKey.nonEmpty ?? Self.apiKeyFromCIForTesting }() // This is modified by CI: diff --git a/Tests/TestingApps/PaywallsTester/PaywallsTester/Views/AppContentView.swift b/Tests/TestingApps/PaywallsTester/PaywallsTester/Views/AppContentView.swift index 1043f35f12..d8d69826a0 100644 --- a/Tests/TestingApps/PaywallsTester/PaywallsTester/Views/AppContentView.swift +++ b/Tests/TestingApps/PaywallsTester/PaywallsTester/Views/AppContentView.swift @@ -86,14 +86,14 @@ struct AppContentView: View { } Spacer() - Button("Reconfigure for demos") { - Purchases.configure(withAPIKey: Configuration.apiKeyForDemos) + Button("Configure for demos") { + Purchases.configure(withAPIKey: Configuration.apiKeyFromCIForDemos) self.observeCustomerInfoStream() } .prominentButtonStyle() - Button("Reconfigure for testing") { - Purchases.configure(withAPIKey: Configuration.apiKeyFromCI) + Button("Configure for testing") { + Purchases.configure(withAPIKey: Configuration.apiKeyFromCIForTesting) self.observeCustomerInfoStream() } .prominentButtonStyle() From d188caa620315778bd4055329b52c6bdefa85d76 Mon Sep 17 00:00:00 2001 From: Andy Boedo Date: Thu, 28 Sep 2023 17:47:28 -0300 Subject: [PATCH 04/19] slight cleanup --- .../PaywallsTester/Views/AppContentView.swift | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/Tests/TestingApps/PaywallsTester/PaywallsTester/Views/AppContentView.swift b/Tests/TestingApps/PaywallsTester/PaywallsTester/Views/AppContentView.swift index d8d69826a0..daa7cfbae3 100644 --- a/Tests/TestingApps/PaywallsTester/PaywallsTester/Views/AppContentView.swift +++ b/Tests/TestingApps/PaywallsTester/PaywallsTester/Views/AppContentView.swift @@ -87,14 +87,12 @@ struct AppContentView: View { Spacer() Button("Configure for demos") { - Purchases.configure(withAPIKey: Configuration.apiKeyFromCIForDemos) - self.observeCustomerInfoStream() + self.reconfigure(with: Configuration.apiKeyFromCIForDemos) } .prominentButtonStyle() Button("Configure for testing") { - Purchases.configure(withAPIKey: Configuration.apiKeyFromCIForTesting) - self.observeCustomerInfoStream() + self.reconfigure(with: Configuration.apiKeyFromCIForTesting) } .prominentButtonStyle() @@ -136,6 +134,11 @@ struct AppContentView: View { } } + private func reconfigure(with apiKey: String) { + Purchases.configure(withAPIKey: apiKey) + self.observeCustomerInfoStream() + } + private func observeCustomerInfoStream() { Task { if Purchases.isConfigured { From 60178f6706851f9579bfeed614a827d23db977e3 Mon Sep 17 00:00:00 2001 From: Andy Boedo Date: Thu, 28 Sep 2023 18:41:27 -0300 Subject: [PATCH 05/19] clean up customer info stream lifecycle --- .../PaywallsTester/Views/AppContentView.swift | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/Tests/TestingApps/PaywallsTester/PaywallsTester/Views/AppContentView.swift b/Tests/TestingApps/PaywallsTester/PaywallsTester/Views/AppContentView.swift index daa7cfbae3..322c472bd8 100644 --- a/Tests/TestingApps/PaywallsTester/PaywallsTester/Views/AppContentView.swift +++ b/Tests/TestingApps/PaywallsTester/PaywallsTester/Views/AppContentView.swift @@ -17,6 +17,10 @@ struct AppContentView: View { @State private var showingDefaultPaywall: Bool = false + @State + private var customerInfoTask: Task<(), Never>? = nil + + var body: some View { TabView { if Purchases.isConfigured { @@ -140,7 +144,8 @@ struct AppContentView: View { } private func observeCustomerInfoStream() { - Task { + self.customerInfoTask?.cancel() + self.customerInfoTask = Task { if Purchases.isConfigured { for await info in Purchases.shared.customerInfoStream { self.customerInfo = info From 51490bacb38b434bee3ee33c90afb0df806846aa Mon Sep 17 00:00:00 2001 From: Andy Boedo Date: Fri, 29 Sep 2023 12:26:22 -0300 Subject: [PATCH 06/19] fix sh script --- .../TestingApps/PaywallsTester/ci_scripts/ci_pre_xcodebuild.sh | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/Tests/TestingApps/PaywallsTester/ci_scripts/ci_pre_xcodebuild.sh b/Tests/TestingApps/PaywallsTester/ci_scripts/ci_pre_xcodebuild.sh index f42e3fd5aa..5f8194807f 100755 --- a/Tests/TestingApps/PaywallsTester/ci_scripts/ci_pre_xcodebuild.sh +++ b/Tests/TestingApps/PaywallsTester/ci_scripts/ci_pre_xcodebuild.sh @@ -6,8 +6,7 @@ SCRIPT_DIR=$( cd -- "$( dirname -- "${BASH_SOURCE[0]}" )" &> /dev/null && pwd ) if [ -z "$PAYWALLS_TESTER_API_KEY_FOR_TESTING" ]; then echo "PaywallsTester API key for testing environment variable is not set." -elif -if [ -z "$PAYWALLS_TESTER_API_KEY_FOR_DEMOS" ]; then +elif [ -z "$PAYWALLS_TESTER_API_KEY_FOR_DEMOS" ]; then echo "PaywallsTester API key for demos environment variable is not set." else echo "Replacing API keys on PaywallsTester" From 8cf27f9ee9839876182f8ccfaf9980cab47efcab Mon Sep 17 00:00:00 2001 From: Andy Boedo Date: Fri, 29 Sep 2023 14:56:06 -0300 Subject: [PATCH 07/19] refactor and cleanup from previous PR --- .../PaywallsTester/Configuration.swift | 47 +++++++++++++++---- .../PaywallsTester/SimpleApp.swift | 15 +----- .../PaywallsTester/Views/AppContentView.swift | 8 ++-- 3 files changed, 42 insertions(+), 28 deletions(-) diff --git a/Tests/TestingApps/PaywallsTester/PaywallsTester/Configuration.swift b/Tests/TestingApps/PaywallsTester/PaywallsTester/Configuration.swift index 439b216b7a..7d28f473ed 100644 --- a/Tests/TestingApps/PaywallsTester/PaywallsTester/Configuration.swift +++ b/Tests/TestingApps/PaywallsTester/PaywallsTester/Configuration.swift @@ -6,6 +6,7 @@ // import Foundation +import RevenueCat enum Configuration { @@ -20,21 +21,47 @@ enum Configuration { } extension Configuration { + enum Mode { + case custom, testing, demos + } - static var effectiveApiKey: String = { - return Self.apiKey.nonEmpty ?? Self.apiKeyFromCIForTesting - }() + static private(set) var currentMode: Mode = Self.apiKey.isEmpty ? .custom : .testing - // This is modified by CI: - static let apiKeyFromCIForTesting = "" - static let apiKeyFromCIForDemos = "" + static var currentAPIKey: String { + switch currentMode { + case .custom: + Self.apiKey + case .testing: + Self.apiKeyFromCIForTesting + case .demos: + Self.apiKeyFromCIForDemos + } + } -} + static func reconfigure(for mode: Mode) { + Self.currentMode = mode + Purchases.configure( + with: .init(withAPIKey: currentAPIKey) + .with(entitlementVerificationMode: .informational) + .with(usesStoreKit2IfAvailable: true) + ) + } -// MARK: - Extensions + static func configure() { + Purchases.logLevel = .verbose + Purchases.proxyURL = Configuration.proxyURL.isEmpty + ? nil + : URL(string: Configuration.proxyURL)! -private extension String { + Purchases.configure( + with: .init(withAPIKey: currentAPIKey) + .with(entitlementVerificationMode: .informational) + .with(usesStoreKit2IfAvailable: true) + ) + } - var nonEmpty: String? { return self.isEmpty ? nil : self } + // This is modified by CI: + static let apiKeyFromCIForTesting = "" + static let apiKeyFromCIForDemos = "" } diff --git a/Tests/TestingApps/PaywallsTester/PaywallsTester/SimpleApp.swift b/Tests/TestingApps/PaywallsTester/PaywallsTester/SimpleApp.swift index 99405040ee..51dae97ec2 100644 --- a/Tests/TestingApps/PaywallsTester/PaywallsTester/SimpleApp.swift +++ b/Tests/TestingApps/PaywallsTester/PaywallsTester/SimpleApp.swift @@ -5,24 +5,13 @@ // 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) - ) + Configuration.configure() } var body: some Scene { @@ -31,6 +20,4 @@ struct SimpleApp: App { } } - private static let apiKeyIsConfigured = !Configuration.effectiveApiKey.isEmpty - } diff --git a/Tests/TestingApps/PaywallsTester/PaywallsTester/Views/AppContentView.swift b/Tests/TestingApps/PaywallsTester/PaywallsTester/Views/AppContentView.swift index 322c472bd8..d52efd3835 100644 --- a/Tests/TestingApps/PaywallsTester/PaywallsTester/Views/AppContentView.swift +++ b/Tests/TestingApps/PaywallsTester/PaywallsTester/Views/AppContentView.swift @@ -91,12 +91,12 @@ struct AppContentView: View { Spacer() Button("Configure for demos") { - self.reconfigure(with: Configuration.apiKeyFromCIForDemos) + self.reconfigure(for: .demos) } .prominentButtonStyle() Button("Configure for testing") { - self.reconfigure(with: Configuration.apiKeyFromCIForTesting) + self.reconfigure(for: .testing) } .prominentButtonStyle() @@ -138,8 +138,8 @@ struct AppContentView: View { } } - private func reconfigure(with apiKey: String) { - Purchases.configure(withAPIKey: apiKey) + private func reconfigure(for mode: Configuration.Mode) { + Configuration.reconfigure(for: mode) self.observeCustomerInfoStream() } From 45bddac1c014c49c9896b734af5d55ea6b26f1ce Mon Sep 17 00:00:00 2001 From: Andy Boedo Date: Fri, 29 Sep 2023 15:39:03 -0300 Subject: [PATCH 08/19] lots of refactors, but the buttons now get enabled / disabled depending on the current mode --- .../PaywallsTester/Configuration.swift | 42 ++++++----- .../PaywallsTester/SimpleApp.swift | 2 +- .../PaywallsTester/Views/AppContentView.swift | 74 +++++++++++++------ .../ci_scripts/ci_pre_xcodebuild.sh | 4 +- 4 files changed, 77 insertions(+), 45 deletions(-) diff --git a/Tests/TestingApps/PaywallsTester/PaywallsTester/Configuration.swift b/Tests/TestingApps/PaywallsTester/PaywallsTester/Configuration.swift index 7d28f473ed..6dba39ec61 100644 --- a/Tests/TestingApps/PaywallsTester/PaywallsTester/Configuration.swift +++ b/Tests/TestingApps/PaywallsTester/PaywallsTester/Configuration.swift @@ -8,29 +8,35 @@ import Foundation import RevenueCat -enum Configuration { +class Configuration: ObservableObject { + static let shared = Configuration() - #warning("Configure API key if you want to test paywalls from your dashboard") + // This is modified by CI: + private static let apiKeyFromCIForTesting = "" + private static let apiKeyFromCIForDemos = "" - // Note: you can leave this empty to use the production server, or point to your own instance. - static let proxyURL = "" - static let apiKey = "" + @Published private(set) var currentMode: Mode - static let entitlement = "pro" + let entitlement = "pro" -} + #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 let proxyURL = "" + private let apiKey = "" -extension Configuration { enum Mode { case custom, testing, demos } - static private(set) var currentMode: Mode = Self.apiKey.isEmpty ? .custom : .testing + private init() { + currentMode = apiKey.isEmpty ? .custom : .testing + } + - static var currentAPIKey: String { + var currentAPIKey: String { switch currentMode { case .custom: - Self.apiKey + self.apiKey case .testing: Self.apiKeyFromCIForTesting case .demos: @@ -38,8 +44,8 @@ extension Configuration { } } - static func reconfigure(for mode: Mode) { - Self.currentMode = mode + func reconfigure(for mode: Mode) { + self.currentMode = mode Purchases.configure( with: .init(withAPIKey: currentAPIKey) .with(entitlementVerificationMode: .informational) @@ -47,11 +53,11 @@ extension Configuration { ) } - static func configure() { + func configure() { Purchases.logLevel = .verbose - Purchases.proxyURL = Configuration.proxyURL.isEmpty + Purchases.proxyURL = self.proxyURL.isEmpty ? nil - : URL(string: Configuration.proxyURL)! + : URL(string: self.proxyURL)! Purchases.configure( with: .init(withAPIKey: currentAPIKey) @@ -60,8 +66,4 @@ extension Configuration { ) } - // This is modified by CI: - static let apiKeyFromCIForTesting = "" - static let apiKeyFromCIForDemos = "" - } diff --git a/Tests/TestingApps/PaywallsTester/PaywallsTester/SimpleApp.swift b/Tests/TestingApps/PaywallsTester/PaywallsTester/SimpleApp.swift index 51dae97ec2..c27151fc67 100644 --- a/Tests/TestingApps/PaywallsTester/PaywallsTester/SimpleApp.swift +++ b/Tests/TestingApps/PaywallsTester/PaywallsTester/SimpleApp.swift @@ -11,7 +11,7 @@ import SwiftUI struct SimpleApp: App { init() { - Configuration.configure() + Configuration.shared.configure() } var body: some Scene { diff --git a/Tests/TestingApps/PaywallsTester/PaywallsTester/Views/AppContentView.swift b/Tests/TestingApps/PaywallsTester/PaywallsTester/Views/AppContentView.swift index d52efd3835..a33f2bca2b 100644 --- a/Tests/TestingApps/PaywallsTester/PaywallsTester/Views/AppContentView.swift +++ b/Tests/TestingApps/PaywallsTester/PaywallsTester/Views/AppContentView.swift @@ -11,6 +11,9 @@ import SwiftUI struct AppContentView: View { + @ObservedObject + private var configuration = Configuration.shared + @State private var customerInfo: CustomerInfo? @@ -89,21 +92,21 @@ struct AppContentView: View { Spacer() } Spacer() - - Button("Configure for demos") { + + Text("Currently configured for \(self.descriptionForCurrentMode())") + .font(.footnote) + + ConfigurationButton(title: "Configure for demos", mode: .demos, configuration: configuration) { self.reconfigure(for: .demos) } - .prominentButtonStyle() - Button("Configure for testing") { + ConfigurationButton(title: "Configure for testing", mode: .testing, configuration: configuration) { self.reconfigure(for: .testing) } - .prominentButtonStyle() - - Button("Present default paywall") { + + ProminentButton(title: "Present default paywall") { showingDefaultPaywall.toggle() } - .prominentButtonStyle() } .padding(.horizontal) .padding(.bottom, 80) @@ -139,7 +142,7 @@ struct AppContentView: View { } private func reconfigure(for mode: Configuration.Mode) { - Configuration.reconfigure(for: mode) + configuration.reconfigure(for: mode) self.observeCustomerInfoStream() } @@ -155,30 +158,57 @@ struct AppContentView: View { } } + 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" + } + + } + } +private struct ProminentButton: View { + var title: String + var action: () -> Void + var background: Color = .accentColor -private extension View { - func prominentButtonStyle() -> some View { - self.modifier(ProminentButtonStyle()) + var body: some View { + Button(action: action) { + Text(title) + .padding() + .frame(maxWidth: .infinity) + .background(background) + .foregroundColor(.white) + .cornerRadius(10) + } } } -private struct ProminentButtonStyle: ViewModifier { - func body(content: Content) -> some View { - content - .buttonStyle(.plain) - .frame(maxWidth: .infinity, maxHeight: 50) - .font(.headline) - .background(Color.accentColor) - .foregroundColor(.white) - .cornerRadius(8) +private struct ConfigurationButton: View { + var title: String + var mode: Configuration.Mode + @ObservedObject var configuration: Configuration + var action: () -> Void + + var body: some View { + ProminentButton( + title: title, + action: action, + background: configuration.currentMode == mode ? Color.gray : Color.accentColor + ) + .disabled(configuration.currentMode == mode) } } extension CustomerInfo { var hasPro: Bool { - return self.entitlements.active.contains { $1.identifier == Configuration.entitlement } + return self.entitlements.active.contains { $1.identifier == Configuration.shared.entitlement } } } diff --git a/Tests/TestingApps/PaywallsTester/ci_scripts/ci_pre_xcodebuild.sh b/Tests/TestingApps/PaywallsTester/ci_scripts/ci_pre_xcodebuild.sh index 5f8194807f..51676fae97 100755 --- a/Tests/TestingApps/PaywallsTester/ci_scripts/ci_pre_xcodebuild.sh +++ b/Tests/TestingApps/PaywallsTester/ci_scripts/ci_pre_xcodebuild.sh @@ -12,7 +12,7 @@ else echo "Replacing API keys on PaywallsTester" file="$SCRIPT_DIR/../PaywallsTester/Configuration.swift" - sed -i.bak 's/static let apiKeyFromCIForTesting = ""/static let apiKeyFromCIForTesting = "'$PAYWALLS_TESTER_API_KEY_FOR_TESTING'"/g' $file - sed -i.bak 's/static let apiKeyFromCIForDemos = ""/static let apiKeyFromCIForDemos = "'$PAYWALLS_TESTER_API_KEY_FOR_DEMOS'"/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 From 04e5623bb01bf1df83cae39fc95ee82e746da1ec Mon Sep 17 00:00:00 2001 From: Andy Boedo Date: Fri, 29 Sep 2023 15:44:35 -0300 Subject: [PATCH 09/19] small style update --- .../PaywallsTester/PaywallsTester/Views/AppContentView.swift | 1 + 1 file changed, 1 insertion(+) diff --git a/Tests/TestingApps/PaywallsTester/PaywallsTester/Views/AppContentView.swift b/Tests/TestingApps/PaywallsTester/PaywallsTester/Views/AppContentView.swift index a33f2bca2b..388c34d3d6 100644 --- a/Tests/TestingApps/PaywallsTester/PaywallsTester/Views/AppContentView.swift +++ b/Tests/TestingApps/PaywallsTester/PaywallsTester/Views/AppContentView.swift @@ -180,6 +180,7 @@ private struct ProminentButton: View { var body: some View { Button(action: action) { Text(title) + .bold() .padding() .frame(maxWidth: .infinity) .background(background) From 10055253bcaccbdbcf2dd99956a2de0bb83d91ed Mon Sep 17 00:00:00 2001 From: Andy Boedo Date: Fri, 29 Sep 2023 15:50:39 -0300 Subject: [PATCH 10/19] fix bug with default config --- .../PaywallsTester/PaywallsTester/Configuration.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Tests/TestingApps/PaywallsTester/PaywallsTester/Configuration.swift b/Tests/TestingApps/PaywallsTester/PaywallsTester/Configuration.swift index 6dba39ec61..f7897792e6 100644 --- a/Tests/TestingApps/PaywallsTester/PaywallsTester/Configuration.swift +++ b/Tests/TestingApps/PaywallsTester/PaywallsTester/Configuration.swift @@ -29,7 +29,7 @@ class Configuration: ObservableObject { } private init() { - currentMode = apiKey.isEmpty ? .custom : .testing + currentMode = apiKey.isEmpty ? .testing : .custom } From 028b379b1385d15064d9314477db5a5b33eb2e54 Mon Sep 17 00:00:00 2001 From: Andy Boedo Date: Mon, 2 Oct 2023 17:59:31 -0300 Subject: [PATCH 11/19] small improvements from PR comments --- .../PaywallsTester/Configuration.swift | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/Tests/TestingApps/PaywallsTester/PaywallsTester/Configuration.swift b/Tests/TestingApps/PaywallsTester/PaywallsTester/Configuration.swift index f7897792e6..bedcb6b478 100644 --- a/Tests/TestingApps/PaywallsTester/PaywallsTester/Configuration.swift +++ b/Tests/TestingApps/PaywallsTester/PaywallsTester/Configuration.swift @@ -8,7 +8,7 @@ import Foundation import RevenueCat -class Configuration: ObservableObject { +final class Configuration: ObservableObject { static let shared = Configuration() // This is modified by CI: @@ -21,22 +21,22 @@ class Configuration: ObservableObject { #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 let proxyURL = "" - private let apiKey = "" + private static let proxyURL = "" + private static let apiKey = "" enum Mode { case custom, testing, demos } private init() { - currentMode = apiKey.isEmpty ? .testing : .custom + self.currentMode = Self.apiKey.isEmpty ? .testing : .custom } var currentAPIKey: String { switch currentMode { case .custom: - self.apiKey + Self.apiKey case .testing: Self.apiKeyFromCIForTesting case .demos: @@ -55,9 +55,9 @@ class Configuration: ObservableObject { func configure() { Purchases.logLevel = .verbose - Purchases.proxyURL = self.proxyURL.isEmpty + Purchases.proxyURL = Self.proxyURL.isEmpty ? nil - : URL(string: self.proxyURL)! + : URL(string: Self.proxyURL)! Purchases.configure( with: .init(withAPIKey: currentAPIKey) From 82777b5f17d7d99129beafb0f5d6f9ebc5235baf Mon Sep 17 00:00:00 2001 From: Andy Boedo Date: Mon, 2 Oct 2023 18:08:39 -0300 Subject: [PATCH 12/19] more refactors from PR comments --- .../PaywallsTester/Configuration.swift | 24 +++++++++---------- 1 file changed, 11 insertions(+), 13 deletions(-) diff --git a/Tests/TestingApps/PaywallsTester/PaywallsTester/Configuration.swift b/Tests/TestingApps/PaywallsTester/PaywallsTester/Configuration.swift index bedcb6b478..b8328cc534 100644 --- a/Tests/TestingApps/PaywallsTester/PaywallsTester/Configuration.swift +++ b/Tests/TestingApps/PaywallsTester/PaywallsTester/Configuration.swift @@ -11,28 +11,27 @@ import RevenueCat final class Configuration: ObservableObject { static let shared = Configuration() - // This is modified by CI: - private static let apiKeyFromCIForTesting = "" - private static let apiKeyFromCIForDemos = "" - @Published private(set) var currentMode: Mode let entitlement = "pro" + enum Mode { + case custom, testing, demos + } + #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 = "" - enum Mode { - case custom, testing, demos - } + // This is modified by CI: + private static let apiKeyFromCIForTesting = "" + private static let apiKeyFromCIForDemos = "" private init() { self.currentMode = Self.apiKey.isEmpty ? .testing : .custom } - var currentAPIKey: String { switch currentMode { case .custom: @@ -46,11 +45,7 @@ final class Configuration: ObservableObject { func reconfigure(for mode: Mode) { self.currentMode = mode - Purchases.configure( - with: .init(withAPIKey: currentAPIKey) - .with(entitlementVerificationMode: .informational) - .with(usesStoreKit2IfAvailable: true) - ) + configureRCSDK() } func configure() { @@ -58,7 +53,10 @@ final class Configuration: ObservableObject { Purchases.proxyURL = Self.proxyURL.isEmpty ? nil : URL(string: Self.proxyURL)! + configureRCSDK() + } + private func configureRCSDK() { Purchases.configure( with: .init(withAPIKey: currentAPIKey) .with(entitlementVerificationMode: .informational) From 3a1ef341e6bd3c9a25f9062ceeff96db1b41e210 Mon Sep 17 00:00:00 2001 From: Andy Boedo Date: Mon, 2 Oct 2023 18:15:58 -0300 Subject: [PATCH 13/19] more updates from PR comments --- .../PaywallsTester/Configuration.swift | 2 +- .../PaywallsTester/Views/AppContentView.swift | 15 +++++++-------- 2 files changed, 8 insertions(+), 9 deletions(-) diff --git a/Tests/TestingApps/PaywallsTester/PaywallsTester/Configuration.swift b/Tests/TestingApps/PaywallsTester/PaywallsTester/Configuration.swift index b8328cc534..25f916099d 100644 --- a/Tests/TestingApps/PaywallsTester/PaywallsTester/Configuration.swift +++ b/Tests/TestingApps/PaywallsTester/PaywallsTester/Configuration.swift @@ -13,7 +13,7 @@ final class Configuration: ObservableObject { @Published private(set) var currentMode: Mode - let entitlement = "pro" + static let entitlement = "pro" enum Mode { case custom, testing, demos diff --git a/Tests/TestingApps/PaywallsTester/PaywallsTester/Views/AppContentView.swift b/Tests/TestingApps/PaywallsTester/PaywallsTester/Views/AppContentView.swift index 388c34d3d6..a96babe34f 100644 --- a/Tests/TestingApps/PaywallsTester/PaywallsTester/Views/AppContentView.swift +++ b/Tests/TestingApps/PaywallsTester/PaywallsTester/Views/AppContentView.swift @@ -142,7 +142,7 @@ struct AppContentView: View { } private func reconfigure(for mode: Configuration.Mode) { - configuration.reconfigure(for: mode) + self.configuration.reconfigure(for: mode) self.observeCustomerInfoStream() } @@ -152,14 +152,13 @@ struct AppContentView: View { if Purchases.isConfigured { for await info in Purchases.shared.customerInfoStream { self.customerInfo = info - self.showingDefaultPaywall = self.showingDefaultPaywall && info.activeSubscriptions.count == 0 + 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" @@ -198,18 +197,18 @@ private struct ConfigurationButton: View { var body: some View { ProminentButton( - title: title, - action: action, - background: configuration.currentMode == mode ? Color.gray : Color.accentColor + title: self.title, + action: self.action, + background: self.configuration.currentMode == mode ? Color.gray : Color.accentColor ) - .disabled(configuration.currentMode == mode) + .disabled(self.configuration.currentMode == mode) } } extension CustomerInfo { var hasPro: Bool { - return self.entitlements.active.contains { $1.identifier == Configuration.shared.entitlement } + return self.entitlements.active.contains { $1.identifier == Configuration.entitlement } } } From 5b261a757bccad4087e83811c4ed66878d6018e4 Mon Sep 17 00:00:00 2001 From: Andy Boedo Date: Mon, 2 Oct 2023 18:17:30 -0300 Subject: [PATCH 14/19] and more --- .../PaywallsTester/Views/AppContentView.swift | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/Tests/TestingApps/PaywallsTester/PaywallsTester/Views/AppContentView.swift b/Tests/TestingApps/PaywallsTester/PaywallsTester/Views/AppContentView.swift index a96babe34f..fb5418f012 100644 --- a/Tests/TestingApps/PaywallsTester/PaywallsTester/Views/AppContentView.swift +++ b/Tests/TestingApps/PaywallsTester/PaywallsTester/Views/AppContentView.swift @@ -177,8 +177,8 @@ private struct ProminentButton: View { var background: Color = .accentColor var body: some View { - Button(action: action) { - Text(title) + Button(action: self.action) { + Text(self.title) .bold() .padding() .frame(maxWidth: .infinity) @@ -190,6 +190,7 @@ private struct ProminentButton: View { } private struct ConfigurationButton: View { + var title: String var mode: Configuration.Mode @ObservedObject var configuration: Configuration @@ -203,6 +204,7 @@ private struct ConfigurationButton: View { ) .disabled(self.configuration.currentMode == mode) } + } extension CustomerInfo { From d7701ce5fd5a19adaa87ab7b66f569de6c1251d4 Mon Sep 17 00:00:00 2001 From: Andy Boedo Date: Mon, 2 Oct 2023 18:18:10 -0300 Subject: [PATCH 15/19] remove extra line break --- .../PaywallsTester/PaywallsTester/Views/AppContentView.swift | 1 - 1 file changed, 1 deletion(-) diff --git a/Tests/TestingApps/PaywallsTester/PaywallsTester/Views/AppContentView.swift b/Tests/TestingApps/PaywallsTester/PaywallsTester/Views/AppContentView.swift index fb5418f012..946ba0234e 100644 --- a/Tests/TestingApps/PaywallsTester/PaywallsTester/Views/AppContentView.swift +++ b/Tests/TestingApps/PaywallsTester/PaywallsTester/Views/AppContentView.swift @@ -23,7 +23,6 @@ struct AppContentView: View { @State private var customerInfoTask: Task<(), Never>? = nil - var body: some View { TabView { if Purchases.isConfigured { From 6a6f6242ff2e0fa70597b01e162fbf14912a0afd Mon Sep 17 00:00:00 2001 From: Andy Boedo Date: Mon, 2 Oct 2023 18:23:50 -0300 Subject: [PATCH 16/19] fixed use case when the app is configured without an API key --- .../PaywallsTester/Configuration.swift | 15 +++++++++++---- .../PaywallsTester/Views/AppContentView.swift | 3 ++- 2 files changed, 13 insertions(+), 5 deletions(-) diff --git a/Tests/TestingApps/PaywallsTester/PaywallsTester/Configuration.swift b/Tests/TestingApps/PaywallsTester/PaywallsTester/Configuration.swift index 25f916099d..cb0dc40319 100644 --- a/Tests/TestingApps/PaywallsTester/PaywallsTester/Configuration.swift +++ b/Tests/TestingApps/PaywallsTester/PaywallsTester/Configuration.swift @@ -16,7 +16,7 @@ final class Configuration: ObservableObject { static let entitlement = "pro" enum Mode { - case custom, testing, demos + case custom, testing, demos, listOnly } #warning("Configure API key if you want to test paywalls from your dashboard") @@ -32,7 +32,7 @@ final class Configuration: ObservableObject { self.currentMode = Self.apiKey.isEmpty ? .testing : .custom } - var currentAPIKey: String { + var currentAPIKey: String? { switch currentMode { case .custom: Self.apiKey @@ -40,12 +40,14 @@ final class Configuration: ObservableObject { Self.apiKeyFromCIForTesting case .demos: Self.apiKeyFromCIForDemos + case .listOnly: + nil } } func reconfigure(for mode: Mode) { self.currentMode = mode - configureRCSDK() + self.configureRCSDK() } func configure() { @@ -53,10 +55,15 @@ final class Configuration: ObservableObject { Purchases.proxyURL = Self.proxyURL.isEmpty ? nil : URL(string: Self.proxyURL)! - configureRCSDK() + self.configureRCSDK() } private func configureRCSDK() { + guard let currentAPIKey = self.currentAPIKey, + !currentAPIKey.isEmpty else { + return + } + Purchases.configure( with: .init(withAPIKey: currentAPIKey) .with(entitlementVerificationMode: .informational) diff --git a/Tests/TestingApps/PaywallsTester/PaywallsTester/Views/AppContentView.swift b/Tests/TestingApps/PaywallsTester/PaywallsTester/Views/AppContentView.swift index 946ba0234e..418b78bcf1 100644 --- a/Tests/TestingApps/PaywallsTester/PaywallsTester/Views/AppContentView.swift +++ b/Tests/TestingApps/PaywallsTester/PaywallsTester/Views/AppContentView.swift @@ -165,8 +165,9 @@ struct AppContentView: View { return "the Paywalls Tester app in RevenueCat Dashboard" case .demos: return "Demos" + case .listOnly: + return "showcasing the different Paywall Templates and Modes available" } - } } From 8d535be5932704a37a18487e1ee618f5cfcc1d3f Mon Sep 17 00:00:00 2001 From: Andy Boedo Date: Tue, 3 Oct 2023 17:58:31 -0300 Subject: [PATCH 17/19] cleaned up configuration flow --- .../PaywallsTester/Configuration.swift | 29 +++++++++---------- .../PaywallsTester/SimpleApp.swift | 4 --- .../PaywallsTester/Views/AppContentView.swift | 18 ++---------- 3 files changed, 16 insertions(+), 35 deletions(-) diff --git a/Tests/TestingApps/PaywallsTester/PaywallsTester/Configuration.swift b/Tests/TestingApps/PaywallsTester/PaywallsTester/Configuration.swift index cb0dc40319..4779498520 100644 --- a/Tests/TestingApps/PaywallsTester/PaywallsTester/Configuration.swift +++ b/Tests/TestingApps/PaywallsTester/PaywallsTester/Configuration.swift @@ -11,11 +11,15 @@ import RevenueCat final class Configuration: ObservableObject { static let shared = Configuration() - @Published private(set) var currentMode: Mode + @Published var currentMode: Mode { + didSet { + configure() + } + } static let entitlement = "pro" - enum Mode { + enum Mode: Equatable { case custom, testing, demos, listOnly } @@ -30,6 +34,12 @@ final class Configuration: ObservableObject { private init() { self.currentMode = Self.apiKey.isEmpty ? .testing : .custom + Purchases.logLevel = .verbose + Purchases.proxyURL = Self.proxyURL.isEmpty + ? nil + : URL(string: Self.proxyURL)! + + self.configure() } var currentAPIKey: String? { @@ -45,20 +55,7 @@ final class Configuration: ObservableObject { } } - func reconfigure(for mode: Mode) { - self.currentMode = mode - self.configureRCSDK() - } - - func configure() { - Purchases.logLevel = .verbose - Purchases.proxyURL = Self.proxyURL.isEmpty - ? nil - : URL(string: Self.proxyURL)! - self.configureRCSDK() - } - - private func configureRCSDK() { + private func configure() { guard let currentAPIKey = self.currentAPIKey, !currentAPIKey.isEmpty else { return diff --git a/Tests/TestingApps/PaywallsTester/PaywallsTester/SimpleApp.swift b/Tests/TestingApps/PaywallsTester/PaywallsTester/SimpleApp.swift index c27151fc67..91d04e9694 100644 --- a/Tests/TestingApps/PaywallsTester/PaywallsTester/SimpleApp.swift +++ b/Tests/TestingApps/PaywallsTester/PaywallsTester/SimpleApp.swift @@ -10,10 +10,6 @@ import SwiftUI @main struct SimpleApp: App { - init() { - Configuration.shared.configure() - } - var body: some Scene { WindowGroup { AppContentView() diff --git a/Tests/TestingApps/PaywallsTester/PaywallsTester/Views/AppContentView.swift b/Tests/TestingApps/PaywallsTester/PaywallsTester/Views/AppContentView.swift index 418b78bcf1..c49ba41fad 100644 --- a/Tests/TestingApps/PaywallsTester/PaywallsTester/Views/AppContentView.swift +++ b/Tests/TestingApps/PaywallsTester/PaywallsTester/Views/AppContentView.swift @@ -96,11 +96,11 @@ struct AppContentView: View { .font(.footnote) ConfigurationButton(title: "Configure for demos", mode: .demos, configuration: configuration) { - self.reconfigure(for: .demos) + self.configuration.currentMode = .demos } ConfigurationButton(title: "Configure for testing", mode: .testing, configuration: configuration) { - self.reconfigure(for: .testing) + self.configuration.currentMode = .testing } ProminentButton(title: "Present default paywall") { @@ -111,9 +111,6 @@ struct AppContentView: View { .padding(.bottom, 80) .frame(maxWidth: .infinity, maxHeight: .infinity) .navigationTitle("Simple App") - .task { - self.observeCustomerInfoStream() - } #if DEBUG .overlay { if #available(iOS 16.0, macOS 13.0, *) { @@ -138,16 +135,7 @@ struct AppContentView: View { #endif } } - } - - private func reconfigure(for mode: Configuration.Mode) { - self.configuration.reconfigure(for: mode) - self.observeCustomerInfoStream() - } - - private func observeCustomerInfoStream() { - self.customerInfoTask?.cancel() - self.customerInfoTask = Task { + .task(id: self.configuration.currentMode) { if Purchases.isConfigured { for await info in Purchases.shared.customerInfoStream { self.customerInfo = info From 55cda38a69337831d31fcd096023ab20c288e491 Mon Sep 17 00:00:00 2001 From: Andy Boedo Date: Tue, 3 Oct 2023 18:02:55 -0300 Subject: [PATCH 18/19] fixed current mode logic --- .../PaywallsTester/PaywallsTester/Configuration.swift | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/Tests/TestingApps/PaywallsTester/PaywallsTester/Configuration.swift b/Tests/TestingApps/PaywallsTester/PaywallsTester/Configuration.swift index 4779498520..537cbd0710 100644 --- a/Tests/TestingApps/PaywallsTester/PaywallsTester/Configuration.swift +++ b/Tests/TestingApps/PaywallsTester/PaywallsTester/Configuration.swift @@ -33,7 +33,12 @@ final class Configuration: ObservableObject { private static let apiKeyFromCIForDemos = "" private init() { - self.currentMode = Self.apiKey.isEmpty ? .testing : .custom + if Self.apiKey.isEmpty { + self.currentMode = Self.apiKeyFromCIForTesting.isEmpty ? .listOnly : .testing + } else { + self.currentMode = .custom + } + Purchases.logLevel = .verbose Purchases.proxyURL = Self.proxyURL.isEmpty ? nil From b1fbb92e15a1ba3c2623a210483c60ab55f0d2cb Mon Sep 17 00:00:00 2001 From: Andy Boedo Date: Wed, 4 Oct 2023 10:27:10 -0300 Subject: [PATCH 19/19] added explicit self --- .../PaywallsTester/PaywallsTester/Configuration.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Tests/TestingApps/PaywallsTester/PaywallsTester/Configuration.swift b/Tests/TestingApps/PaywallsTester/PaywallsTester/Configuration.swift index 537cbd0710..b07dbcc0af 100644 --- a/Tests/TestingApps/PaywallsTester/PaywallsTester/Configuration.swift +++ b/Tests/TestingApps/PaywallsTester/PaywallsTester/Configuration.swift @@ -13,7 +13,7 @@ final class Configuration: ObservableObject { @Published var currentMode: Mode { didSet { - configure() + self.configure() } }