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

Paywalls: improved default paywall configuration #2926

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
2 changes: 1 addition & 1 deletion RevenueCatUI/Data/TestData.swift
Original file line number Diff line number Diff line change
Expand Up @@ -321,7 +321,7 @@ internal enum TestData {
background: Self.paywallBackgroundImageName,
icon: Self.paywallHeaderImageName
)
static let paywallAssetBaseURL = URL(string: "https://d35rwhxn1vk1te.cloudfront.net")!
static let paywallAssetBaseURL = URL(string: "https://assets.pawwalls.com")!
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Not strictly required for this PR but updating this now.


private static let offeringIdentifier = "offering"

Expand Down
3 changes: 3 additions & 0 deletions RevenueCatUI/Data/Variables.swift
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ extension PaywallData.LocalizedConfiguration {
/// A type that can provide necessary information for `VariableHandler` to replace variable content in strings.
protocol VariableDataProvider {

var applicationName: String { get }

var isMonthly: Bool { get }

var localizedPrice: String { get }
Expand Down Expand Up @@ -85,6 +87,7 @@ private extension VariableDataProvider {

func value(for variableName: String, locale: Locale) -> String {
switch variableName {
case "app_name": return self.applicationName
Copy link
Contributor Author

Choose a reason for hiding this comment

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

I made this a "secret" variable so the default template can display the app name as a title.
I don't think it makes sense to expose it in the docs / frontend, since users can just write whatever their app name is there, it doesn't have to be dynamic.

Thoughts @RevenueCat/cashnip?

case "price": return self.localizedPrice
case "price_per_month": return self.localizedPricePerMonth
case "total_price_and_per_month":
Expand Down
14 changes: 14 additions & 0 deletions RevenueCatUI/Helpers/Package+VariableDataProvider.swift
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,10 @@ import RevenueCat
@available(iOS 16.0, macOS 13.0, tvOS 16.0, *)
extension Package: VariableDataProvider {

var applicationName: String {
return Bundle.main.applicationDisplayName
}

var isMonthly: Bool {
return self.packageType == .monthly
}
Expand Down Expand Up @@ -57,3 +61,13 @@ private extension Package {
}

}

private extension Bundle {

var applicationDisplayName: String {
return self.object(forInfoDictionaryKey: "CFBundleDisplayName") as? String
?? self.object(forInfoDictionaryKey: "CFBundleName") as? String
?? ""
}

}
19 changes: 10 additions & 9 deletions RevenueCatUI/Helpers/PaywallData+Default.swift
Original file line number Diff line number Diff line change
Expand Up @@ -36,27 +36,28 @@ private extension PaywallData {
light: .init(
background: try! .init(stringRepresentation: "#FFFFFF"),
text1: try! .init(stringRepresentation: "#000000"),
callToActionBackground: try! .init(stringRepresentation: "#EC807C"),
callToActionBackground: try! .init(stringRepresentation: "#FF8181"),
callToActionForeground: try! .init(stringRepresentation: "#FFFFFF"),
accent1: try! .init(stringRepresentation: "#EC807C")
accent1: try! .init(stringRepresentation: "#BC66FF"),
accent2: try! .init(stringRepresentation: "#111111")
),
dark: .init(
background: try! .init(stringRepresentation: "#000000"),
text1: try! .init(stringRepresentation: "#FFFFFF"),
callToActionBackground: try! .init(stringRepresentation: "#ACD27A"),
callToActionForeground: try! .init(stringRepresentation: "#000000"),
accent1: try! .init(stringRepresentation: "#ACD27A")
accent1: try! .init(stringRepresentation: "#BC66FF"),
accent2: try! .init(stringRepresentation: "#EEEEEE")
)
)
// swiftlint:enable force_try

static let localization: PaywallData.LocalizedConfiguration = .init(
title: "Subscription",
subtitle: "Unlock access",
callToAction: "Purchase",
offerDetails: "{{ price_per_month }} per month",
offerDetailsWithIntroOffer: "Start your {{ intro_duration }} trial, then {{ price_per_month }} per month",
features: []
title: "{{ app_name }}",
subtitle: "Unlock full access with these subscriptions:",
callToAction: "Continue",
offerDetails: "{{ total_price_and_per_month }}.",
offerDetailsWithIntroOffer: "Start your {{ intro_duration }} trial, then {{ total_price_and_per_month }}."
)

static let backgroundImage = "background.jpg"
Expand Down
10 changes: 8 additions & 2 deletions Tests/RevenueCatUITests/Data/VariablesTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,11 @@ class VariablesTests: TestCase {
expect(self.process("{{price_per_month}}")) == "{{price_per_month}}"
}

func testApplicationName() {
self.provider.applicationName = "Paywalls"
expect(self.process("Welcome to {{ app_name }}")) == "Welcome to Paywalls"
}

func testPrice() {
self.provider.localizedPrice = "$10.99"
expect(self.process("Purchase for {{ price }}")) == "Purchase for $10.99"
Expand Down Expand Up @@ -90,7 +95,7 @@ class VariablesTests: TestCase {

func testProcessesLocalizedConfiguration() {
let configuration = PaywallData.LocalizedConfiguration(
title: "Title {{ product_name }}",
title: "Buy {{ product_name }} for {{ app_name }}",
subtitle: "Price: {{ price }}",
callToAction: "Unlock {{ product_name }} for {{ price_per_month }}",
callToActionWithIntroOffer: "Start your {{ intro_duration }} free trial\n" +
Expand All @@ -110,7 +115,7 @@ class VariablesTests: TestCase {
)
let processed = configuration.processVariables(with: TestData.packageWithIntroOffer)

expect(processed.title) == "Title PRO monthly"
expect(processed.title) == "Buy PRO monthly for xctest"
expect(processed.subtitle) == "Price: $3.99"
expect(processed.callToAction) == "Unlock PRO monthly for $3.99"
expect(processed.callToActionWithIntroOffer) == "Start your 1 week free trial\nThen $3.99 every month"
Expand Down Expand Up @@ -142,6 +147,7 @@ private extension VariablesTests {

private struct MockVariableProvider: VariableDataProvider {

var applicationName: String = ""
var isMonthly: Bool = false
var localizedPrice: String = ""
var localizedPricePerMonth: String = ""
Expand Down
9 changes: 9 additions & 0 deletions Tests/RevenueCatUITests/Templates/OtherPaywallViewTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,15 @@ class OtherPaywallViewTests: BaseSnapshotTest {
view.snapshot(size: Self.fullScreenSize)
}

func testDefaultDarkModePaywall() {
let view = PaywallView(offering: Self.offeringWithNoPaywall,
introEligibility: Self.eligibleChecker,
purchaseHandler: Self.purchaseHandler)
.environment(\.colorScheme, .dark)

view.snapshot(size: Self.fullScreenSize)
}

func testLoadingPaywallView() {
let view = LoadingPaywallView()
view.snapshot(size: Self.fullScreenSize)
Expand Down
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.