-
Notifications
You must be signed in to change notification settings - Fork 307
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
: new .onPurchaseStarted(package)
modifier
#3693
Merged
Merged
Changes from all commits
Commits
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -37,13 +37,15 @@ extension PaywallPresentationMode { | |
|
||
} | ||
|
||
// swiftlint:disable file_length | ||
@available(iOS 15.0, macOS 12.0, tvOS 15.0, watchOS 8.0, *) | ||
@available(macOS, unavailable, message: "RevenueCatUI does not support macOS yet") | ||
@available(tvOS, unavailable, message: "RevenueCatUI does not support tvOS yet") | ||
extension View { | ||
|
||
typealias CustomerInfoFetcher = @Sendable () async throws -> CustomerInfo | ||
|
||
// swiftlint:disable line_length | ||
/// Presents a ``PaywallView`` if the given entitlement identifier is not active | ||
/// in the current environment for the current `CustomerInfo`. | ||
/// ```swift | ||
|
@@ -65,18 +67,77 @@ extension View { | |
/// [Documentation](https://rev.cat/paywalls) | ||
/// | ||
/// - Tag: presentPaywallIfNeeded | ||
@available(iOS, deprecated: 1, renamed: "presentPaywallIfNeeded(requiredEntitlementIdentifier:offering:fonts:presentationMode:purchaseStarted:purchaseCompleted:purchaseCancelled:restoreStarted:restoreCompleted:purchaseFailure:restoreFailure:onDismiss:)") | ||
@available(tvOS, deprecated: 1, renamed: "presentPaywallIfNeeded(requiredEntitlementIdentifier:offering:fonts:presentationMode:purchaseStarted:purchaseCompleted:purchaseCancelled:restoreStarted:restoreCompleted:purchaseFailure:restoreFailure:onDismiss:)") | ||
@available(watchOS, deprecated: 1, renamed: "presentPaywallIfNeeded(requiredEntitlementIdentifier:offering:fonts:presentationMode:purchaseStarted:purchaseCompleted:purchaseCancelled:restoreStarted:restoreCompleted:purchaseFailure:restoreFailure:onDismiss:)") | ||
@available(macOS, deprecated: 1, renamed: "presentPaywallIfNeeded(requiredEntitlementIdentifier:offering:fonts:presentationMode:purchaseStarted:purchaseCompleted:purchaseCancelled:restoreStarted:restoreCompleted:purchaseFailure:restoreFailure:onDismiss:)") | ||
@available(macCatalyst, deprecated: 1, renamed: "presentPaywallIfNeeded(requiredEntitlementIdentifier:offering:fonts:presentationMode:purchaseStarted:purchaseCompleted:purchaseCancelled:restoreStarted:restoreCompleted:purchaseFailure:restoreFailure:onDismiss:)") | ||
// swiftlint:enable line_length | ||
public func presentPaywallIfNeeded( | ||
requiredEntitlementIdentifier: String, | ||
offering: Offering? = nil, | ||
fonts: PaywallFontProvider = DefaultPaywallFontProvider(), | ||
presentationMode: PaywallPresentationMode = .default, | ||
purchaseStarted: PurchaseStartedHandler? = nil, | ||
purchaseStarted: @escaping PurchaseStartedHandler, | ||
purchaseCompleted: PurchaseOrRestoreCompletedHandler? = nil, | ||
purchaseCancelled: PurchaseCancelledHandler? = nil, | ||
restoreCompleted: PurchaseOrRestoreCompletedHandler? = nil, | ||
purchaseFailure: PurchaseFailureHandler? = nil, | ||
restoreFailure: PurchaseFailureHandler? = nil, | ||
onDismiss: (() -> Void)? = nil | ||
) -> some View { | ||
return self.presentPaywallIfNeeded( | ||
requiredEntitlementIdentifier: requiredEntitlementIdentifier, | ||
offering: offering, | ||
fonts: fonts, | ||
presentationMode: presentationMode, | ||
purchaseStarted: { _ in | ||
purchaseStarted() | ||
}, | ||
purchaseCompleted: purchaseCompleted, | ||
purchaseCancelled: purchaseCancelled, | ||
restoreStarted: nil, | ||
restoreCompleted: restoreCompleted, | ||
purchaseFailure: purchaseFailure, | ||
restoreFailure: restoreFailure, | ||
onDismiss: onDismiss | ||
) | ||
} | ||
|
||
/// Presents a ``PaywallView`` if the given entitlement identifier is not active | ||
/// in the current environment for the current `CustomerInfo`. | ||
/// ```swift | ||
/// var body: some View { | ||
/// YourApp() | ||
/// .presentPaywallIfNeeded(requiredEntitlementIdentifier: "pro") | ||
/// } | ||
/// ``` | ||
/// - Note: If loading the `CustomerInfo` fails (for example, if Internet is offline), | ||
/// the paywall won't be displayed. | ||
/// | ||
/// - Parameter offering: The `Offering` containing the desired `PaywallData` to display. | ||
/// If `nil` (the default), `Offerings.current` will be used. Note that specifying this parameter means | ||
/// that it will ignore the offering configured in an active experiment. | ||
/// - Parameter fonts: An optional ``PaywallFontProvider``. | ||
/// - Parameter presentationMode: The desired presentation mode of the paywall. Defaults to `.sheet`. | ||
/// | ||
/// ### Related Articles | ||
/// [Documentation](https://rev.cat/paywalls) | ||
/// | ||
/// - Tag: presentPaywallIfNeeded | ||
public func presentPaywallIfNeeded( | ||
requiredEntitlementIdentifier: String, | ||
offering: Offering? = nil, | ||
fonts: PaywallFontProvider = DefaultPaywallFontProvider(), | ||
presentationMode: PaywallPresentationMode = .default, | ||
purchaseStarted: PurchaseOfPackageStartedHandler? = nil, | ||
purchaseCompleted: PurchaseOrRestoreCompletedHandler? = nil, | ||
purchaseCancelled: PurchaseCancelledHandler? = nil, | ||
restoreStarted: RestoreStartedHandler? = nil, | ||
restoreCompleted: PurchaseOrRestoreCompletedHandler? = nil, | ||
purchaseFailure: PurchaseFailureHandler? = nil, | ||
restoreFailure: PurchaseFailureHandler? = nil, | ||
onDismiss: (() -> Void)? = nil | ||
) -> some View { | ||
return self.presentPaywallIfNeeded( | ||
offering: offering, | ||
|
@@ -98,6 +159,7 @@ extension View { | |
) | ||
} | ||
|
||
// swiftlint:disable line_length | ||
/// Presents a ``PaywallView`` based a given condition. | ||
/// Example: | ||
/// ```swift | ||
|
@@ -134,12 +196,18 @@ extension View { | |
/// | ||
/// ### Related Articles | ||
/// [Documentation](https://rev.cat/paywalls) | ||
@available(iOS, deprecated: 1, renamed: "presentPaywallIfNeeded(offering:fonts:presentationMode:shouldDisplay:purchaseStarted:purchaseCompleted:purchaseCancelled:restoreStarted:restoreCompleted:purchaseFailure:restoreFailure:onDismiss:)") | ||
@available(tvOS, deprecated: 1, renamed: "presentPaywallIfNeeded(offering:fonts:presentationMode:shouldDisplay:purchaseStarted:purchaseCompleted:purchaseCancelled:restoreStarted:restoreCompleted:purchaseFailure:restoreFailure:onDismiss:)") | ||
@available(watchOS, deprecated: 1, renamed: "presentPaywallIfNeeded(offering:fonts:presentationMode:shouldDisplay:purchaseStarted:purchaseCompleted:purchaseCancelled:restoreStarted:restoreCompleted:purchaseFailure:restoreFailure:onDismiss:)") | ||
@available(macOS, deprecated: 1, renamed: "presentPaywallIfNeeded(offering:fonts:presentationMode:shouldDisplay:purchaseStarted:purchaseCompleted:purchaseCancelled:restoreStarted:restoreCompleted:purchaseFailure:restoreFailure:onDismiss:)") | ||
@available(macCatalyst, deprecated: 1, renamed: "presentPaywallIfNeeded(offering:fonts:presentationMode:shouldDisplay:purchaseStarted:purchaseCompleted:purchaseCancelled:restoreStarted:restoreCompleted:purchaseFailure:restoreFailure:onDismiss:)") | ||
// swiftlint:enable line_length | ||
public func presentPaywallIfNeeded( | ||
offering: Offering? = nil, | ||
fonts: PaywallFontProvider = DefaultPaywallFontProvider(), | ||
presentationMode: PaywallPresentationMode = .default, | ||
shouldDisplay: @escaping @Sendable (CustomerInfo) -> Bool, | ||
purchaseStarted: PurchaseStartedHandler? = nil, | ||
purchaseStarted: @escaping PurchaseStartedHandler, | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Same here. If someone was not passing anything or passing let's say just |
||
purchaseCompleted: PurchaseOrRestoreCompletedHandler? = nil, | ||
purchaseCancelled: PurchaseCancelledHandler? = nil, | ||
restoreCompleted: PurchaseOrRestoreCompletedHandler? = nil, | ||
|
@@ -152,7 +220,9 @@ extension View { | |
fonts: fonts, | ||
presentationMode: presentationMode, | ||
shouldDisplay: shouldDisplay, | ||
purchaseStarted: purchaseStarted, | ||
purchaseStarted: { _ in | ||
purchaseStarted() | ||
}, | ||
purchaseCompleted: purchaseCompleted, | ||
purchaseCancelled: purchaseCancelled, | ||
restoreStarted: nil, | ||
|
@@ -170,6 +240,81 @@ extension View { | |
) | ||
} | ||
|
||
/// Presents a ``PaywallView`` based a given condition. | ||
/// Example: | ||
/// ```swift | ||
/// var body: some View { | ||
/// YourApp() | ||
/// .presentPaywallIfNeeded { | ||
/// !$0.entitlements.active.keys.contains("entitlement_identifier") | ||
/// } purchaseStarted: { package in | ||
/// print("Purchase started \(package)") | ||
/// } purchaseCompleted: { customerInfo in | ||
/// print("Customer info unlocked entitlement: \(customerInfo.entitlements)") | ||
/// } purchaseCancelled: { | ||
/// print("Purchase was cancelled") | ||
/// } restoreStarted: { | ||
/// print("Restore started") | ||
/// } restoreCompleted: { customerInfo in | ||
/// // If `entitlement_identifier` is active, paywall will dismiss automatically. | ||
/// print("Purchases restored") | ||
/// } purchaseFailure: { error in | ||
/// print("Error purchasing: \(error)") | ||
/// } restoreFailure: { error in | ||
/// print("Error restoring purchases: \(error)") | ||
/// } onDismiss: { | ||
/// print("Paywall was dismissed either manually or automatically after a purchase.") | ||
/// } | ||
/// } | ||
/// ``` | ||
/// - Note: If loading the `CustomerInfo` fails (for example, if Internet is offline), | ||
/// the paywall won't be displayed. | ||
/// | ||
/// - Parameter offering: The `Offering` containing the desired `PaywallData` to display. | ||
/// If `nil` (the default), `Offerings.current` will be used. Note that specifying this parameter means | ||
/// that it will ignore the offering configured in an active experiment. | ||
/// - Parameter fonts: An optional ``PaywallFontProvider``. | ||
/// - Parameter presentationMode: The desired presentation mode of the paywall. Defaults to `.sheet`. | ||
/// | ||
/// ### Related Articles | ||
/// [Documentation](https://rev.cat/paywalls) | ||
public func presentPaywallIfNeeded( | ||
offering: Offering? = nil, | ||
fonts: PaywallFontProvider = DefaultPaywallFontProvider(), | ||
presentationMode: PaywallPresentationMode = .default, | ||
shouldDisplay: @escaping @Sendable (CustomerInfo) -> Bool, | ||
purchaseStarted: PurchaseOfPackageStartedHandler? = nil, | ||
purchaseCompleted: PurchaseOrRestoreCompletedHandler? = nil, | ||
purchaseCancelled: PurchaseCancelledHandler? = nil, | ||
restoreStarted: RestoreStartedHandler? = nil, | ||
restoreCompleted: PurchaseOrRestoreCompletedHandler? = nil, | ||
purchaseFailure: PurchaseFailureHandler? = nil, | ||
restoreFailure: PurchaseFailureHandler? = nil, | ||
onDismiss: (() -> Void)? = nil | ||
) -> some View { | ||
return self.presentPaywallIfNeeded( | ||
offering: offering, | ||
fonts: fonts, | ||
presentationMode: presentationMode, | ||
shouldDisplay: shouldDisplay, | ||
purchaseStarted: purchaseStarted, | ||
purchaseCompleted: purchaseCompleted, | ||
purchaseCancelled: purchaseCancelled, | ||
restoreStarted: restoreStarted, | ||
restoreCompleted: restoreCompleted, | ||
purchaseFailure: purchaseFailure, | ||
restoreFailure: restoreFailure, | ||
onDismiss: onDismiss, | ||
customerInfoFetcher: { | ||
guard Purchases.isConfigured else { | ||
throw PaywallError.purchasesNotConfigured | ||
} | ||
|
||
return try await Purchases.shared.customerInfo() | ||
} | ||
) | ||
} | ||
|
||
// Visible overload for tests | ||
func presentPaywallIfNeeded( | ||
offering: Offering? = nil, | ||
|
@@ -178,7 +323,7 @@ extension View { | |
purchaseHandler: PurchaseHandler? = nil, | ||
presentationMode: PaywallPresentationMode = .default, | ||
shouldDisplay: @escaping @Sendable (CustomerInfo) -> Bool, | ||
purchaseStarted: PurchaseStartedHandler? = nil, | ||
purchaseStarted: PurchaseOfPackageStartedHandler? = nil, | ||
purchaseCompleted: PurchaseOrRestoreCompletedHandler? = nil, | ||
purchaseCancelled: PurchaseCancelledHandler? = nil, | ||
restoreStarted: RestoreStartedHandler? = nil, | ||
|
@@ -222,7 +367,7 @@ private struct PresentingPaywallModifier: ViewModifier { | |
|
||
var shouldDisplay: @Sendable (CustomerInfo) -> Bool | ||
var presentationMode: PaywallPresentationMode | ||
var purchaseStarted: PurchaseStartedHandler? | ||
var purchaseStarted: PurchaseOfPackageStartedHandler? | ||
var purchaseCompleted: PurchaseOrRestoreCompletedHandler? | ||
var purchaseCancelled: PurchaseCancelledHandler? | ||
var restoreCompleted: PurchaseOrRestoreCompletedHandler? | ||
|
@@ -240,7 +385,7 @@ private struct PresentingPaywallModifier: ViewModifier { | |
init( | ||
shouldDisplay: @escaping @Sendable (CustomerInfo) -> Bool, | ||
presentationMode: PaywallPresentationMode, | ||
purchaseStarted: PurchaseStartedHandler?, | ||
purchaseStarted: PurchaseOfPackageStartedHandler?, | ||
purchaseCompleted: PurchaseOrRestoreCompletedHandler?, | ||
purchaseCancelled: PurchaseCancelledHandler?, | ||
restoreCompleted: PurchaseOrRestoreCompletedHandler?, | ||
|
@@ -319,7 +464,7 @@ private struct PresentingPaywallModifier: ViewModifier { | |
) | ||
) | ||
.onPurchaseStarted { | ||
self.purchaseStarted?() | ||
self.purchaseStarted?($0) | ||
} | ||
.onPurchaseCompleted { | ||
self.purchaseCompleted?($0) | ||
|
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Made this non nullable so the function wouldn't collide if someone was only passing
requiredEntitlementIdentifier
. I also had to made it@escaping
. Is that considered a breaking change?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@vegaro I'm not certain if the
@escaping
is breaking changing (I'd have to experiment) but making this non-nullable is a breaking change, isn't it? 😬If somebody was using this function without
purchaseStarted
, it will now require them to havepurchaseStarted
?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Did the API tester for this break? That would really let us know, wouldn't it? 🤷♂️
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It didn't fail, that's why I am not sure 🤷♂️
If they were not passing it, it will use the new non-deprecated function automatically (that one has it as nullable)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ahhh, okay! That makes sense then 😊 I think this should be good then!