diff --git a/RevenueCatUI/Data/TemplateViewConfiguration.swift b/RevenueCatUI/Data/TemplateViewConfiguration.swift index 205ed0e115..4f65b96038 100644 --- a/RevenueCatUI/Data/TemplateViewConfiguration.swift +++ b/RevenueCatUI/Data/TemplateViewConfiguration.swift @@ -31,6 +31,7 @@ extension TemplateViewConfiguration { let content: RevenueCat.Package let localization: ProcessedLocalizedConfiguration + let currentlySubscribed: Bool let discountRelativeToMostExpensivePerMonth: Double? } @@ -98,8 +99,10 @@ extension TemplateViewConfiguration.PackageConfiguration { /// Creates a `PackageConfiguration` based on `setting`. /// - Throws: `TemplateError` + // swiftlint:disable:next function_parameter_count static func create( with packages: [RevenueCat.Package], + activelySubscribedProductIdentifiers: Set, filter: [String], default: String?, localization: PaywallData.LocalizedConfiguration, @@ -117,6 +120,9 @@ extension TemplateViewConfiguration.PackageConfiguration { TemplateViewConfiguration.Package( content: package, localization: localization.processVariables(with: package, locale: locale), + currentlySubscribed: activelySubscribedProductIdentifiers.contains( + package.storeProduct.productIdentifier + ), discountRelativeToMostExpensivePerMonth: Self.discount( from: package.storeProduct.pricePerMonth?.doubleValue, relativeTo: mostExpensivePricePerMonth diff --git a/RevenueCatUI/Helpers/PreviewHelpers.swift b/RevenueCatUI/Helpers/PreviewHelpers.swift index 9128b2fd65..3255ac1d45 100644 --- a/RevenueCatUI/Helpers/PreviewHelpers.swift +++ b/RevenueCatUI/Helpers/PreviewHelpers.swift @@ -53,6 +53,7 @@ struct PreviewableTemplate: View { init( offering: Offering, + activelySubscribedProductIdentifiers: Set = [], mode: PaywallViewMode = .default, presentInSheet: Bool = false, creator: @escaping Creator @@ -61,6 +62,7 @@ struct PreviewableTemplate: View { self.configuration = paywall.configuration( for: offering, + activelySubscribedProductIdentifiers: activelySubscribedProductIdentifiers, template: PaywallTemplate(rawValue: paywall.templateName)!, mode: mode, fonts: DefaultPaywallFontProvider(), diff --git a/RevenueCatUI/PaywallView.swift b/RevenueCatUI/PaywallView.swift index b084a204d9..e0e5b7f58d 100644 --- a/RevenueCatUI/PaywallView.swift +++ b/RevenueCatUI/PaywallView.swift @@ -20,6 +20,8 @@ public struct PaywallView: View { @State private var offering: Offering? @State + private var customerInfo: CustomerInfo? + @State private var error: NSError? /// Create a view that loads the `Offerings.current`. @@ -32,6 +34,7 @@ public struct PaywallView: View { ) { self.init( offering: nil, + customerInfo: nil, fonts: fonts, introEligibility: .default(), purchaseHandler: .default() @@ -48,6 +51,7 @@ public struct PaywallView: View { ) { self.init( offering: offering, + customerInfo: nil, fonts: fonts, introEligibility: .default(), purchaseHandler: .default() @@ -56,12 +60,14 @@ public struct PaywallView: View { init( offering: Offering?, + customerInfo: CustomerInfo?, mode: PaywallViewMode = .default, fonts: PaywallFontProvider = DefaultPaywallFontProvider(), introEligibility: TrialOrIntroEligibilityChecker?, purchaseHandler: PurchaseHandler? ) { self._offering = .init(initialValue: offering) + self._customerInfo = .init(initialValue: customerInfo) self.introEligibility = introEligibility self.purchaseHandler = purchaseHandler self.mode = mode @@ -79,8 +85,9 @@ public struct PaywallView: View { private var content: some View { VStack { // Necessary to work around FB12674350 and FB12787354 if let checker = self.introEligibility, let purchaseHandler = self.purchaseHandler { - if let offering = self.offering { + if let offering = self.offering, let customerInfo = self.customerInfo { self.paywallView(for: offering, + activelySubscribedProductIdentifiers: customerInfo.activeSubscriptions, fonts: self.fonts, checker: checker, purchaseHandler: purchaseHandler) @@ -94,11 +101,16 @@ public struct PaywallView: View { throw PaywallError.purchasesNotConfigured } - guard let offering = try await Purchases.shared.offerings().current else { - throw PaywallError.noCurrentOffering + if self.offering == nil { + guard let offering = try await Purchases.shared.offerings().current else { + throw PaywallError.noCurrentOffering + } + self.offering = offering } - self.offering = offering + if self.customerInfo == nil { + self.customerInfo = try await Purchases.shared.customerInfo() + } } catch let error as NSError { self.error = error } @@ -113,6 +125,7 @@ public struct PaywallView: View { @ViewBuilder private func paywallView( for offering: Offering, + activelySubscribedProductIdentifiers: Set, fonts: PaywallFontProvider, checker: TrialOrIntroEligibilityChecker, purchaseHandler: PurchaseHandler @@ -121,6 +134,7 @@ public struct PaywallView: View { let paywallView = LoadedOfferingPaywallView( offering: offering, + activelySubscribedProductIdentifiers: activelySubscribedProductIdentifiers, paywall: paywall, template: template, mode: self.mode, @@ -151,6 +165,7 @@ public struct PaywallView: View { struct LoadedOfferingPaywallView: View { private let offering: Offering + private let activelySubscribedProductIdentifiers: Set private let paywall: PaywallData private let template: PaywallTemplate private let mode: PaywallViewMode @@ -166,6 +181,7 @@ struct LoadedOfferingPaywallView: View { init( offering: Offering, + activelySubscribedProductIdentifiers: Set, paywall: PaywallData, template: PaywallTemplate, mode: PaywallViewMode, @@ -174,6 +190,7 @@ struct LoadedOfferingPaywallView: View { purchaseHandler: PurchaseHandler ) { self.offering = offering + self.activelySubscribedProductIdentifiers = activelySubscribedProductIdentifiers self.paywall = paywall self.template = template self.mode = mode @@ -187,6 +204,7 @@ struct LoadedOfferingPaywallView: View { var body: some View { let view = self.paywall .createView(for: self.offering, + activelySubscribedProductIdentifiers: self.activelySubscribedProductIdentifiers, template: self.template, mode: self.mode, fonts: self.fonts, @@ -227,6 +245,7 @@ struct PaywallView_Previews: PreviewProvider { ForEach(Self.modes, id: \.self) { mode in PaywallView( offering: offering, + customerInfo: TestData.customerInfo, mode: mode, introEligibility: PreviewHelpers.introEligibilityChecker, purchaseHandler: PreviewHelpers.purchaseHandler diff --git a/RevenueCatUI/Templates/TemplateViewType.swift b/RevenueCatUI/Templates/TemplateViewType.swift index f1b1c85fc1..75539fc555 100644 --- a/RevenueCatUI/Templates/TemplateViewType.swift +++ b/RevenueCatUI/Templates/TemplateViewType.swift @@ -62,12 +62,14 @@ extension PaywallData { @ViewBuilder // swiftlint:disable:next function_parameter_count func createView(for offering: Offering, + activelySubscribedProductIdentifiers: Set, template: PaywallTemplate, mode: PaywallViewMode, fonts: PaywallFontProvider, introEligibility: IntroEligibilityViewModel, locale: Locale) -> some View { switch self.configuration(for: offering, + activelySubscribedProductIdentifiers: activelySubscribedProductIdentifiers, template: template, mode: mode, fonts: fonts, @@ -84,8 +86,10 @@ extension PaywallData { } } + // swiftlint:disable:next function_parameter_count func configuration( for offering: Offering, + activelySubscribedProductIdentifiers: Set, template: PaywallTemplate, mode: PaywallViewMode, fonts: PaywallFontProvider, @@ -95,6 +99,7 @@ extension PaywallData { TemplateViewConfiguration( mode: mode, packages: try .create(with: offering.availablePackages, + activelySubscribedProductIdentifiers: activelySubscribedProductIdentifiers, filter: self.config.packages, default: self.config.defaultPackage, localization: self.localizedConfiguration, diff --git a/RevenueCatUI/View+PresentPaywall.swift b/RevenueCatUI/View+PresentPaywall.swift index 5f1b1a6e6d..ae64f5db3e 100644 --- a/RevenueCatUI/View+PresentPaywall.swift +++ b/RevenueCatUI/View+PresentPaywall.swift @@ -105,6 +105,11 @@ extension View { @available(tvOS, unavailable) private struct PresentingPaywallModifier: ViewModifier { + private struct Data: Identifiable { + var customerInfo: CustomerInfo + var id: String { self.customerInfo.originalAppUserId } + } + var shouldDisplay: @Sendable (CustomerInfo) -> Bool var purchaseCompleted: PurchaseCompletedHandler? var offering: Offering? @@ -115,14 +120,15 @@ private struct PresentingPaywallModifier: ViewModifier { var purchaseHandler: PurchaseHandler? @State - private var isDisplayed = false + private var data: Data? func body(content: Content) -> some View { content - .sheet(isPresented: self.$isDisplayed) { + .sheet(item: self.$data) { data in NavigationView { PaywallView( offering: self.offering, + customerInfo: data.customerInfo, fonts: self.fontProvider, introEligibility: self.introEligibility ?? .default(), purchaseHandler: self.purchaseHandler ?? .default() @@ -130,12 +136,12 @@ private struct PresentingPaywallModifier: ViewModifier { .onPurchaseCompleted { self.purchaseCompleted?($0) - self.isDisplayed = false + self.data = nil } .toolbar { ToolbarItem(placement: .destructiveAction) { Button { - self.isDisplayed = false + self.data = nil } label: { Image(systemName: "xmark") } @@ -151,7 +157,7 @@ private struct PresentingPaywallModifier: ViewModifier { if self.shouldDisplay(info) { Logger.debug(Strings.displaying_paywall) - self.isDisplayed = true + self.data = .init(customerInfo: info) } else { Logger.debug(Strings.not_displaying_paywall) } diff --git a/RevenueCatUI/View+PresentPaywallFooter.swift b/RevenueCatUI/View+PresentPaywallFooter.swift index bc7a6983a1..dd9b3e6460 100644 --- a/RevenueCatUI/View+PresentPaywallFooter.swift +++ b/RevenueCatUI/View+PresentPaywallFooter.swift @@ -30,6 +30,7 @@ extension View { ) -> some View { return self.paywallFooter( offering: nil, + customerInfo: nil, condensed: condensed, fonts: fonts, introEligibility: nil, @@ -55,6 +56,7 @@ extension View { ) -> some View { return self.paywallFooter( offering: offering, + customerInfo: nil, condensed: condensed, fonts: fonts, introEligibility: nil, @@ -64,6 +66,7 @@ extension View { func paywallFooter( offering: Offering?, + customerInfo: CustomerInfo?, condensed: Bool = false, fonts: PaywallFontProvider = DefaultPaywallFontProvider(), introEligibility: TrialOrIntroEligibilityChecker? = nil, @@ -73,6 +76,7 @@ extension View { return self .modifier(PresentingPaywallFooterModifier( offering: offering, + customerInfo: customerInfo, condensed: condensed, purchaseCompleted: purchaseCompleted, fontProvider: fonts, @@ -88,6 +92,7 @@ extension View { private struct PresentingPaywallFooterModifier: ViewModifier { let offering: Offering? + let customerInfo: CustomerInfo? let condensed: Bool let purchaseCompleted: PurchaseCompletedHandler? @@ -100,6 +105,7 @@ private struct PresentingPaywallFooterModifier: ViewModifier { .safeAreaInset(edge: .bottom) { PaywallView( offering: self.offering, + customerInfo: self.customerInfo, mode: self.condensed ? .condensedFooter : .footer, fonts: self.fontProvider, introEligibility: self.introEligibility ?? .default(), diff --git a/RevenueCatUI/Views/LoadingPaywallView.swift b/RevenueCatUI/Views/LoadingPaywallView.swift index 2e8e7dd735..2c2a29b30a 100644 --- a/RevenueCatUI/Views/LoadingPaywallView.swift +++ b/RevenueCatUI/Views/LoadingPaywallView.swift @@ -26,6 +26,7 @@ struct LoadingPaywallView: View { paywall: Self.defaultPaywall, availablePackages: Self.packages ), + activelySubscribedProductIdentifiers: [], paywall: Self.defaultPaywall, template: Self.template, mode: self.mode, diff --git a/RevenueCatUI/Views/PurchaseButton.swift b/RevenueCatUI/Views/PurchaseButton.swift index e1d3094580..bb9528ae63 100644 --- a/RevenueCatUI/Views/PurchaseButton.swift +++ b/RevenueCatUI/Views/PurchaseButton.swift @@ -83,6 +83,7 @@ struct PurchaseButton: View { .buttonStyle(.borderedProminent) .frame(maxWidth: .infinity) .dynamicTypeSize(...Constants.maximumDynamicTypeSize) + .disabled(self.package.currentlySubscribed) } } @@ -156,6 +157,7 @@ struct PurchaseButton_Previews: PreviewProvider { content: TestData.packageWithIntroOffer, localization: TestData.localization1.processVariables(with: TestData.packageWithIntroOffer, locale: .current), + currentlySubscribed: Bool.random(), discountRelativeToMostExpensivePerMonth: nil ) } diff --git a/Tests/RevenueCatUITests/BaseSnapshotTest.swift b/Tests/RevenueCatUITests/BaseSnapshotTest.swift index 521228f7ea..c8cdfe14bd 100644 --- a/Tests/RevenueCatUITests/BaseSnapshotTest.swift +++ b/Tests/RevenueCatUITests/BaseSnapshotTest.swift @@ -5,6 +5,7 @@ // Created by Nacho Soto on 7/17/23. // import Nimble +import RevenueCat @testable import RevenueCatUI import SnapshotTesting import SwiftUI @@ -20,6 +21,21 @@ class BaseSnapshotTest: TestCase { // isRecording = true } + static func createPaywall( + offering: Offering, + mode: PaywallViewMode = .default, + fonts: PaywallFontProvider = DefaultPaywallFontProvider(), + introEligibility: TrialOrIntroEligibilityChecker = BaseSnapshotTest.eligibleChecker, + purchaseHandler: PurchaseHandler = BaseSnapshotTest.purchaseHandler + ) -> some View { + return PaywallView(offering: offering, + customerInfo: TestData.customerInfo, + mode: mode, + fonts: fonts, + introEligibility: eligibleChecker, + purchaseHandler: purchaseHandler) + } + } @available(iOS 15.0, macOS 12.0, tvOS 15.0, watchOS 8.0, *) diff --git a/Tests/RevenueCatUITests/Data/TemplateViewConfigurationTests.swift b/Tests/RevenueCatUITests/Data/TemplateViewConfigurationTests.swift index 8392241aa6..aaabe3144f 100644 --- a/Tests/RevenueCatUITests/Data/TemplateViewConfigurationTests.swift +++ b/Tests/RevenueCatUITests/Data/TemplateViewConfigurationTests.swift @@ -20,6 +20,7 @@ class TemplateViewConfigurationCreationTests: BaseTemplateViewConfigurationTests expect { try Config.create( with: [], + activelySubscribedProductIdentifiers: [], filter: [PackageType.monthly.identifier], default: nil, localization: TestData.paywallWithIntroOffer.localizedConfiguration, @@ -32,6 +33,7 @@ class TemplateViewConfigurationCreationTests: BaseTemplateViewConfigurationTests expect { try Config.create( with: [TestData.monthlyPackage], + activelySubscribedProductIdentifiers: [], filter: [], default: nil, localization: TestData.paywallWithIntroOffer.localizedConfiguration, @@ -43,6 +45,7 @@ class TemplateViewConfigurationCreationTests: BaseTemplateViewConfigurationTests func testCreateSinglePackage() throws { let result = try Config.create( with: [TestData.monthlyPackage], + activelySubscribedProductIdentifiers: [], filter: [PackageType.monthly.identifier], default: nil, localization: Self.localization, @@ -52,6 +55,29 @@ class TemplateViewConfigurationCreationTests: BaseTemplateViewConfigurationTests switch result { case let .single(package): expect(package.content) === TestData.monthlyPackage + expect(package.currentlySubscribed) == false + expect(package.discountRelativeToMostExpensivePerMonth).to(beNil()) + Self.verifyLocalizationWasProcessed(package.localization, for: TestData.monthlyPackage) + case .multiple: + fail("Invalid result: \(result)") + } + } + + func testCreateSingleSubscribedPackage() throws { + let result = try Config.create( + with: [TestData.monthlyPackage], + activelySubscribedProductIdentifiers: [TestData.monthlyPackage.storeProduct.productIdentifier, + "Anotoher product"], + filter: [PackageType.monthly.identifier], + default: nil, + localization: Self.localization, + setting: .single + ) + + switch result { + case let .single(package): + expect(package.content) === TestData.monthlyPackage + expect(package.currentlySubscribed) == true expect(package.discountRelativeToMostExpensivePerMonth).to(beNil()) Self.verifyLocalizationWasProcessed(package.localization, for: TestData.monthlyPackage) case .multiple: @@ -62,6 +88,7 @@ class TemplateViewConfigurationCreationTests: BaseTemplateViewConfigurationTests func testCreateOnlyLifetime() throws { let result = try Config.create( with: [TestData.lifetimePackage], + activelySubscribedProductIdentifiers: [], filter: [PackageType.lifetime.identifier], default: nil, localization: Self.localization, @@ -84,6 +111,10 @@ class TemplateViewConfigurationCreationTests: BaseTemplateViewConfigurationTests TestData.weeklyPackage, TestData.lifetimePackage, Self.consumable], + activelySubscribedProductIdentifiers: [ + TestData.monthlyPackage.storeProduct.productIdentifier, + TestData.lifetimePackage.storeProduct.productIdentifier + ], filter: [PackageType.annual.identifier, PackageType.monthly.identifier, PackageType.lifetime.identifier, @@ -104,21 +135,25 @@ class TemplateViewConfigurationCreationTests: BaseTemplateViewConfigurationTests let annual = packages[0] expect(annual.content) === TestData.annualPackage + expect(annual.currentlySubscribed) == false expect(annual.discountRelativeToMostExpensivePerMonth) .to(beCloseTo(0.36, within: 0.01)) Self.verifyLocalizationWasProcessed(annual.localization, for: TestData.annualPackage) let monthly = packages[1] expect(monthly.content) === TestData.monthlyPackage + expect(monthly.currentlySubscribed) == true expect(monthly.discountRelativeToMostExpensivePerMonth).to(beNil()) Self.verifyLocalizationWasProcessed(monthly.localization, for: TestData.monthlyPackage) let lifetime = packages[2] expect(lifetime.content) === TestData.lifetimePackage + expect(lifetime.currentlySubscribed) == true Self.verifyLocalizationWasProcessed(lifetime.localization, for: TestData.lifetimePackage) let consumable = packages[3] expect(consumable.content) === Self.consumable + expect(consumable.currentlySubscribed) == false Self.verifyLocalizationWasProcessed(consumable.localization, for: Self.consumable) } } diff --git a/Tests/RevenueCatUITests/PaywallFooterTests.swift b/Tests/RevenueCatUITests/PaywallFooterTests.swift index 616c0e4ae6..d8513a4ee4 100644 --- a/Tests/RevenueCatUITests/PaywallFooterTests.swift +++ b/Tests/RevenueCatUITests/PaywallFooterTests.swift @@ -28,6 +28,7 @@ class PaywallFooterTests: TestCase { try Text("") .paywallFooter(offering: Self.offering, + customerInfo: TestData.customerInfo, introEligibility: .producing(eligibility: .eligible), purchaseHandler: Self.purchaseHandler) { customerInfo = $0 diff --git a/Tests/RevenueCatUITests/PurchaseCompletedHandlerTests.swift b/Tests/RevenueCatUITests/PurchaseCompletedHandlerTests.swift index 4bbe68dfcd..f4a5ae22be 100644 --- a/Tests/RevenueCatUITests/PurchaseCompletedHandlerTests.swift +++ b/Tests/RevenueCatUITests/PurchaseCompletedHandlerTests.swift @@ -25,6 +25,7 @@ class PurchaseCompletedHandlerTests: TestCase { try PaywallView( offering: Self.offering.withLocalImages, + customerInfo: TestData.customerInfo, introEligibility: .producing(eligibility: .eligible), purchaseHandler: handler ) @@ -48,6 +49,7 @@ class PurchaseCompletedHandlerTests: TestCase { try PaywallView( offering: Self.offering.withLocalImages, + customerInfo: TestData.customerInfo, introEligibility: .producing(eligibility: .eligible), purchaseHandler: Self.purchaseHandler ) diff --git a/Tests/RevenueCatUITests/Templates/OtherPaywallViewTests.swift b/Tests/RevenueCatUITests/Templates/OtherPaywallViewTests.swift index 3012d66a9f..59f6838feb 100644 --- a/Tests/RevenueCatUITests/Templates/OtherPaywallViewTests.swift +++ b/Tests/RevenueCatUITests/Templates/OtherPaywallViewTests.swift @@ -16,20 +16,14 @@ import SnapshotTesting class OtherPaywallViewTests: BaseSnapshotTest { func testDefaultPaywall() { - let view = PaywallView(offering: TestData.offeringWithNoPaywall, - introEligibility: Self.eligibleChecker, - purchaseHandler: Self.purchaseHandler) - - view.snapshot(size: Self.fullScreenSize) + Self.createPaywall(offering: TestData.offeringWithNoPaywall) + .snapshot(size: Self.fullScreenSize) } func testDefaultDarkModePaywall() { - let view = PaywallView(offering: TestData.offeringWithNoPaywall, - introEligibility: Self.eligibleChecker, - purchaseHandler: Self.purchaseHandler) + Self.createPaywall(offering: TestData.offeringWithNoPaywall) .environment(\.colorScheme, .dark) - - view.snapshot(size: Self.fullScreenSize) + .snapshot(size: Self.fullScreenSize) } func testLoadingPaywallView() { diff --git a/Tests/RevenueCatUITests/Templates/PaywallViewDynamicTypeTests.swift b/Tests/RevenueCatUITests/Templates/PaywallViewDynamicTypeTests.swift index bebb26bb2c..117b2f08ac 100644 --- a/Tests/RevenueCatUITests/Templates/PaywallViewDynamicTypeTests.swift +++ b/Tests/RevenueCatUITests/Templates/PaywallViewDynamicTypeTests.swift @@ -65,11 +65,11 @@ private extension PaywallViewDynamicTypeTests { } private static func createView(_ type: DynamicTypeSize) -> some View { - let offering = TestData.offeringWithIntroOffer - - return PaywallView(offering: offering.withLocalImages, - introEligibility: Self.eligibleChecker, - purchaseHandler: Self.purchaseHandler) + return Self.createPaywall( + offering: TestData + .offeringWithIntroOffer + .withLocalImages + ) .dynamicTypeSize(type) } diff --git a/Tests/RevenueCatUITests/Templates/PaywallViewLocalizationTests.swift b/Tests/RevenueCatUITests/Templates/PaywallViewLocalizationTests.swift index efb9b4c474..006eb767e8 100644 --- a/Tests/RevenueCatUITests/Templates/PaywallViewLocalizationTests.swift +++ b/Tests/RevenueCatUITests/Templates/PaywallViewLocalizationTests.swift @@ -30,7 +30,7 @@ private extension PaywallViewLocalizationTests { } private static func createView() -> some View { - return PaywallView( + return Self.createPaywall( offering: Self.offering.withLocalImages, introEligibility: .init(checker: { packages in return Dictionary( @@ -43,8 +43,7 @@ private extension PaywallViewLocalizationTests { return (package, result) } ) - }), - purchaseHandler: Self.purchaseHandler + }) ) } diff --git a/Tests/RevenueCatUITests/Templates/Template1ViewTests.swift b/Tests/RevenueCatUITests/Templates/Template1ViewTests.swift index 42cf20ed1f..e7418de188 100644 --- a/Tests/RevenueCatUITests/Templates/Template1ViewTests.swift +++ b/Tests/RevenueCatUITests/Templates/Template1ViewTests.swift @@ -10,54 +10,41 @@ import SwiftUI class Template1ViewTests: BaseSnapshotTest { func testSamplePaywall() { - PaywallView(offering: Self.offeringWithNoIntroOffer, - introEligibility: Self.eligibleChecker, - purchaseHandler: Self.purchaseHandler) - .snapshot(size: Self.fullScreenSize) + Self.createPaywall(offering: Self.offeringWithNoIntroOffer) + .snapshot(size: Self.fullScreenSize) } func testCustomFont() { - PaywallView(offering: Self.offeringWithNoIntroOffer, - fonts: Self.fonts, - introEligibility: Self.eligibleChecker, - purchaseHandler: Self.purchaseHandler) + Self.createPaywall(offering: Self.offeringWithNoIntroOffer, + fonts: Self.fonts) .snapshot(size: Self.fullScreenSize) } func testFooterPaywall() { - PaywallView(offering: Self.offeringWithNoIntroOffer, - mode: .footer, - introEligibility: Self.eligibleChecker, - purchaseHandler: Self.purchaseHandler) + Self.createPaywall(offering: Self.offeringWithNoIntroOffer, + mode: .footer) .snapshot(size: Self.footerSize) } func testCondensedFooterPaywall() { - PaywallView(offering: Self.offeringWithNoIntroOffer, - mode: .condensedFooter, - introEligibility: Self.eligibleChecker, - purchaseHandler: Self.purchaseHandler) + Self.createPaywall(offering: Self.offeringWithNoIntroOffer, + mode: .condensedFooter) .snapshot(size: Self.footerSize) } func testSamplePaywallWithIntroOffer() { - let view = PaywallView(offering: Self.offeringWithIntroOffer, - introEligibility: Self.eligibleChecker, - purchaseHandler: Self.purchaseHandler) - - view.snapshot(size: Self.fullScreenSize) + Self.createPaywall(offering: Self.offeringWithIntroOffer) + .snapshot(size: Self.fullScreenSize) } func testSamplePaywallWithIneligibleIntroOffer() { - let view = PaywallView(offering: Self.offeringWithIntroOffer, - introEligibility: Self.ineligibleChecker, - purchaseHandler: Self.purchaseHandler) - - view.snapshot(size: Self.fullScreenSize) + Self.createPaywall(offering: Self.offeringWithIntroOffer, + introEligibility: Self.ineligibleChecker) + .snapshot(size: Self.fullScreenSize) } func testSamplePaywallWithLoadingEligibility() { - let view = PaywallView( + let view = Self.createPaywall( offering: Self.offeringWithIntroOffer, introEligibility: Self.ineligibleChecker .with(delay: 30), @@ -68,11 +55,8 @@ class Template1ViewTests: BaseSnapshotTest { } func testDarkMode() { - let view = PaywallView(offering: Self.offeringWithIntroOffer, - introEligibility: Self.ineligibleChecker, - purchaseHandler: Self.purchaseHandler) - - view + Self.createPaywall(offering: Self.offeringWithIntroOffer, + introEligibility: Self.ineligibleChecker) .environment(\.colorScheme, .dark) .snapshot(size: Self.fullScreenSize) } diff --git a/Tests/RevenueCatUITests/Templates/Template2ViewTests.swift b/Tests/RevenueCatUITests/Templates/Template2ViewTests.swift index 5527c0a711..538739afa3 100644 --- a/Tests/RevenueCatUITests/Templates/Template2ViewTests.swift +++ b/Tests/RevenueCatUITests/Templates/Template2ViewTests.swift @@ -9,42 +9,33 @@ import SnapshotTesting class Template2ViewTests: BaseSnapshotTest { func testSamplePaywall() { - PaywallView(offering: Self.offering.withLocalImages, - introEligibility: Self.eligibleChecker, - purchaseHandler: Self.purchaseHandler) - .snapshot(size: Self.fullScreenSize) + Self.createPaywall(offering: Self.offering.withLocalImages) + .snapshot(size: Self.fullScreenSize) } func testCustomFont() { - PaywallView(offering: Self.offering.withLocalImages, - fonts: Self.fonts, - introEligibility: Self.eligibleChecker, - purchaseHandler: Self.purchaseHandler) + Self.createPaywall(offering: Self.offering.withLocalImages, + fonts: Self.fonts) .snapshot(size: Self.fullScreenSize) } func testFooterPaywall() { - PaywallView(offering: Self.offering.withLocalImages, - mode: .footer, - introEligibility: Self.eligibleChecker, - purchaseHandler: Self.purchaseHandler) + Self.createPaywall(offering: Self.offering.withLocalImages, + mode: .footer) .snapshot(size: Self.footerSize) } func testCondensedFooterPaywall() { - PaywallView(offering: Self.offering.withLocalImages, - mode: .condensedFooter, - introEligibility: Self.eligibleChecker, - purchaseHandler: Self.purchaseHandler) + Self.createPaywall(offering: Self.offering.withLocalImages, + mode: .condensedFooter) .snapshot(size: Self.footerSize) } func testPurchasingState() { let handler = Self.purchaseHandler.with(delay: 120) - let view = PaywallView(offering: Self.offering.withLocalImages, - introEligibility: Self.eligibleChecker, - purchaseHandler: handler) + let view = Self.createPaywall(offering: Self.offering.withLocalImages, + purchaseHandler: handler) .task { _ = try? await handler.purchase(package: TestData.annualPackage, with: .fullScreen) @@ -54,11 +45,8 @@ class Template2ViewTests: BaseSnapshotTest { } func testDarkMode() { - let view = PaywallView(offering: Self.offering.withLocalImages, - introEligibility: Self.ineligibleChecker, - purchaseHandler: Self.purchaseHandler) - - view + Self.createPaywall(offering: Self.offering.withLocalImages, + introEligibility: Self.ineligibleChecker) .environment(\.colorScheme, .dark) .snapshot(size: Self.fullScreenSize) } diff --git a/Tests/RevenueCatUITests/Templates/Template3ViewTests.swift b/Tests/RevenueCatUITests/Templates/Template3ViewTests.swift index ce0fbfadd8..722c0bc2c0 100644 --- a/Tests/RevenueCatUITests/Templates/Template3ViewTests.swift +++ b/Tests/RevenueCatUITests/Templates/Template3ViewTests.swift @@ -9,41 +9,31 @@ import SnapshotTesting class Template3ViewTests: BaseSnapshotTest { func testSamplePaywall() { - PaywallView(offering: Self.offering.withLocalImages, - introEligibility: Self.eligibleChecker, - purchaseHandler: Self.purchaseHandler) - .snapshot(size: Self.fullScreenSize) + Self.createPaywall(offering: Self.offering.withLocalImages) + .snapshot(size: Self.fullScreenSize) } func testDarkMode() { - PaywallView(offering: Self.offering.withLocalImages, - introEligibility: Self.eligibleChecker, - purchaseHandler: Self.purchaseHandler) - .environment(\.colorScheme, .dark) - .snapshot(size: Self.fullScreenSize) + Self.createPaywall(offering: Self.offering.withLocalImages) + .environment(\.colorScheme, .dark) + .snapshot(size: Self.fullScreenSize) } func testCustomFont() { - PaywallView(offering: Self.offering.withLocalImages, - fonts: Self.fonts, - introEligibility: Self.eligibleChecker, - purchaseHandler: Self.purchaseHandler) + Self.createPaywall(offering: Self.offering.withLocalImages, + fonts: Self.fonts) .snapshot(size: Self.fullScreenSize) } func testFooterPaywall() { - PaywallView(offering: Self.offering.withLocalImages, - mode: .footer, - introEligibility: Self.eligibleChecker, - purchaseHandler: Self.purchaseHandler) + Self.createPaywall(offering: Self.offering.withLocalImages, + mode: .footer) .snapshot(size: Self.footerSize) } func testCondensedFooterPaywall() { - PaywallView(offering: Self.offering.withLocalImages, - mode: .condensedFooter, - introEligibility: Self.eligibleChecker, - purchaseHandler: Self.purchaseHandler) + Self.createPaywall(offering: Self.offering.withLocalImages, + mode: .condensedFooter) .snapshot(size: Self.footerSize) } diff --git a/Tests/RevenueCatUITests/Templates/Template4ViewTests.swift b/Tests/RevenueCatUITests/Templates/Template4ViewTests.swift index 5cc78ec701..746582d78c 100644 --- a/Tests/RevenueCatUITests/Templates/Template4ViewTests.swift +++ b/Tests/RevenueCatUITests/Templates/Template4ViewTests.swift @@ -9,49 +9,37 @@ import SnapshotTesting class Template4ViewTests: BaseSnapshotTest { func testSamplePaywall() { - PaywallView(offering: Self.offering.withLocalImages, - introEligibility: Self.eligibleChecker, - purchaseHandler: Self.purchaseHandler) - .snapshot(size: Self.fullScreenSize) + Self.createPaywall(offering: Self.offering.withLocalImages) + .snapshot(size: Self.fullScreenSize) } func testCustomFont() { - PaywallView(offering: Self.offering.withLocalImages, - fonts: Self.fonts, - introEligibility: Self.eligibleChecker, - purchaseHandler: Self.purchaseHandler) + Self.createPaywall(offering: Self.offering.withLocalImages, + fonts: Self.fonts) .snapshot(size: Self.fullScreenSize) } func testLargeDynamicType() { - PaywallView(offering: Self.offering.withLocalImages, - introEligibility: Self.eligibleChecker, - purchaseHandler: Self.purchaseHandler) - .environment(\.dynamicTypeSize, .xxLarge) - .snapshot(size: Self.fullScreenSize) + Self.createPaywall(offering: Self.offering.withLocalImages) + .environment(\.dynamicTypeSize, .xxLarge) + .snapshot(size: Self.fullScreenSize) } func testLargerDynamicType() { - PaywallView(offering: Self.offering.withLocalImages, - introEligibility: Self.eligibleChecker, - purchaseHandler: Self.purchaseHandler) - .environment(\.dynamicTypeSize, .accessibility2) - .snapshot(size: Self.fullScreenSize) + Self.createPaywall(offering: Self.offering.withLocalImages) + .environment(\.dynamicTypeSize, .accessibility2) + .snapshot(size: Self.fullScreenSize) } func testFooterPaywall() { - PaywallView(offering: Self.offering.withLocalImages, - mode: .footer, - introEligibility: Self.eligibleChecker, - purchaseHandler: Self.purchaseHandler) + Self.createPaywall(offering: Self.offering.withLocalImages, + mode: .footer) .snapshot(size: Self.footerSize) } func testCondensedFooterPaywall() { - PaywallView(offering: Self.offering.withLocalImages, - mode: .condensedFooter, - introEligibility: Self.eligibleChecker, - purchaseHandler: Self.purchaseHandler) + Self.createPaywall(offering: Self.offering.withLocalImages, + mode: .condensedFooter) .snapshot(size: Self.footerSize) } diff --git a/Tests/TestingApps/SimpleApp/SimpleApp/SamplePaywalls.swift b/Tests/TestingApps/SimpleApp/SimpleApp/SamplePaywalls.swift index 71f152f62e..0f5e5cf86a 100644 --- a/Tests/TestingApps/SimpleApp/SimpleApp/SamplePaywalls.swift +++ b/Tests/TestingApps/SimpleApp/SimpleApp/SamplePaywalls.swift @@ -53,6 +53,8 @@ final class SamplePaywallLoader { ) } + let customerInfo = TestData.customerInfo + private func paywall(for template: PaywallTemplate) -> PaywallData { switch template { case .template1: diff --git a/Tests/TestingApps/SimpleApp/SimpleApp/Views/CustomPaywall.swift b/Tests/TestingApps/SimpleApp/SimpleApp/Views/CustomPaywall.swift index baf561b546..1faa156121 100644 --- a/Tests/TestingApps/SimpleApp/SimpleApp/Views/CustomPaywall.swift +++ b/Tests/TestingApps/SimpleApp/SimpleApp/Views/CustomPaywall.swift @@ -12,6 +12,7 @@ import SwiftUI struct CustomPaywall: View { var offering: Offering? + var customerInfo: CustomerInfo? var condensed: Bool var introEligibility: TrialOrIntroEligibilityChecker? var purchaseHandler: PurchaseHandler? @@ -36,6 +37,7 @@ struct CustomPaywall: View { .frame(maxWidth: .infinity) .scrollableIfNecessary(.vertical) .paywallFooter(offering: self.offering, + customerInfo: self.customerInfo, condensed: self.condensed, fonts: DefaultPaywallFontProvider(), introEligibility: self.introEligibility ?? .default(), @@ -69,6 +71,7 @@ struct CustomPaywall_Previews: PreviewProvider { ForEach(Self.condensedOptions, id: \.self) { mode in CustomPaywall( offering: TestData.offeringWithMultiPackagePaywall, + customerInfo: TestData.customerInfo, condensed: mode, introEligibility: .producing(eligibility: .eligible), purchaseHandler: .mock() @@ -79,6 +82,7 @@ struct CustomPaywall_Previews: PreviewProvider { ForEach(Self.condensedOptions, id: \.self) { mode in CustomPaywall( offering: TestData.offeringWithMultiPackageHorizontalPaywall, + customerInfo: TestData.customerInfo, condensed: mode, introEligibility: .producing(eligibility: .eligible), purchaseHandler: .mock() diff --git a/Tests/TestingApps/SimpleApp/SimpleApp/Views/SamplePaywallsList.swift b/Tests/TestingApps/SimpleApp/SimpleApp/Views/SamplePaywallsList.swift index 337f2d6345..5ca59c4e48 100644 --- a/Tests/TestingApps/SimpleApp/SimpleApp/Views/SamplePaywallsList.swift +++ b/Tests/TestingApps/SimpleApp/SimpleApp/Views/SamplePaywallsList.swift @@ -27,11 +27,13 @@ struct SamplePaywallsList: View { switch mode { case .fullScreen: PaywallView(offering: Self.loader.offering(for: template), + customerInfo: Self.loader.customerInfo, introEligibility: Self.introEligibility, purchaseHandler: .default()) case .footer, .condensedFooter: CustomPaywall(offering: Self.loader.offering(for: template), + customerInfo: Self.loader.customerInfo, condensed: mode == .condensedFooter, introEligibility: Self.introEligibility, purchaseHandler: .default()) @@ -39,20 +41,24 @@ struct SamplePaywallsList: View { case let .customFont(template): PaywallView(offering: Self.loader.offering(for: template), + customerInfo: Self.loader.customerInfo, fonts: Self.customFontProvider, introEligibility: Self.introEligibility, purchaseHandler: .default()) case let .customPaywall(mode): - CustomPaywall(condensed: mode == .condensedFooter) + CustomPaywall(customerInfo: Self.loader.customerInfo, + condensed: mode == .condensedFooter) case .missingPaywall: PaywallView(offering: Self.loader.offeringWithDefaultPaywall(), + customerInfo: Self.loader.customerInfo, introEligibility: Self.introEligibility, purchaseHandler: .default()) case .unrecognizedPaywall: PaywallView(offering: Self.loader.offeringWithUnrecognizedPaywall(), + customerInfo: Self.loader.customerInfo, introEligibility: Self.introEligibility, purchaseHandler: .default()) }