Skip to content

Commit

Permalink
Paywalls: add methods for presenting paywalls with an offering iden…
Browse files Browse the repository at this point in the history
…tifier

Depends on RevenueCat/purchases-ios#3587.
  • Loading branch information
NachoSoto committed Jan 19, 2024
1 parent 5a312fb commit ffa8bad
Show file tree
Hide file tree
Showing 2 changed files with 112 additions and 49 deletions.
18 changes: 18 additions & 0 deletions ios/PurchasesHybridCommon/ObjCAPITester/RCPaywallProxyAPITest.m
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@

#import <Foundation/Foundation.h>
@import PurchasesHybridCommon;
@import RevenueCat;
@import UIKit;

NS_ASSUME_NONNULL_BEGIN

Expand All @@ -19,18 +21,34 @@ @implementation RCPaywallProxyAPITest

- (void)testAPI {
if (@available(iOS 15.0, *)) {
RCOffering *offering;

PaywallProxy *proxy = [PaywallProxy new];
[proxy presentPaywall];
[proxy presentPaywallWithDisplayCloseButton:true];
[proxy presentPaywallWithPaywallResultHandler:^(NSString *result) {}];
[proxy presentPaywallWithDisplayCloseButton:true paywallResultHandler:^(NSString *result) {}];
[proxy presentPaywallWithOffering:offering displayCloseButton:true paywallResultHandler:^(NSString *result) {}];
[proxy presentPaywallWithOfferingIdentifier:@"offering"
displayCloseButton:true
paywallResultHandler:^(NSString *result) {}];

[proxy presentPaywallIfNeededWithRequiredEntitlementIdentifier:@""];
[proxy presentPaywallIfNeededWithRequiredEntitlementIdentifier:@"" displayCloseButton:YES];
[proxy presentPaywallIfNeededWithRequiredEntitlementIdentifier:@""
paywallResultHandler:^(NSString *result) {}];
[proxy presentPaywallIfNeededWithRequiredEntitlementIdentifier:@""
displayCloseButton:true
paywallResultHandler:^(NSString *result) {}];
[proxy presentPaywallIfNeededWithRequiredEntitlementIdentifier:@""
offeringIdentifier:@"offering"
displayCloseButton:true
paywallResultHandler:^(NSString *result) {}];

__unused UIViewController *view1 = [proxy createPaywallView];
__unused UIViewController *view2 = [proxy createPaywallViewWithOfferingIdentifier:@"offering"];
__unused UIViewController *footer1 = [proxy createFooterPaywallView];
__unused UIViewController *footer2 = [proxy createFooterPaywallViewWithOfferingIdentifier:@"offering"];
}
}

Expand Down
143 changes: 94 additions & 49 deletions ios/PurchasesHybridCommon/PurchasesHybridCommon/PaywallProxy.swift
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,12 @@ import UIKit

@objc
public func createPaywallView() -> UIViewController {
return UIHostingController(rootView: PaywallView())
return PaywallViewController()
}

@objc
public func createPaywallView(offeringIdentifier: String) -> UIViewController {
return PaywallViewController(offeringIdentifier: offeringIdentifier)
}

@objc
Expand All @@ -36,46 +41,28 @@ import UIKit
return controller
}

@available(*, deprecated, message: "Use presentPaywall with paywallResultHandler instead")
@objc
public func presentPaywall() {
self.privatePresentPaywall(displayCloseButton: nil, offering: nil)
}

@available(*, deprecated, message: "Use presentPaywall with paywallResultHandler instead")
@objc
public func presentPaywall(displayCloseButton: Bool) {
self.privatePresentPaywall(displayCloseButton: displayCloseButton, offering: nil)
}

@available(*, deprecated, message: "Use presentPaywall with paywallResultHandler instead")
@objc
public func presentPaywall(offering: Offering) {
self.privatePresentPaywall(displayCloseButton: nil, offering: offering)
}
public func createFooterPaywallView(offeringIdentifier: String) -> UIViewController {
let controller = PaywallFooterViewController(offeringIdentifier: offeringIdentifier)
controller.delegate = self

@available(*, deprecated, message: "Use presentPaywall with paywallResultHandler instead")
@objc
public func presentPaywall(offering: Offering, displayCloseButton: Bool) {
self.privatePresentPaywall(displayCloseButton: displayCloseButton, offering: offering)
return controller
}

@objc
public func presentPaywall(paywallResultHandler: @escaping (String) -> Void) {
self.privatePresentPaywall(displayCloseButton: nil, offering: nil, paywallResultHandler: paywallResultHandler)
self.privatePresentPaywall(paywallResultHandler: paywallResultHandler)
}

@objc
public func presentPaywall(displayCloseButton: Bool, paywallResultHandler: @escaping (String) -> Void) {
self.privatePresentPaywall(displayCloseButton: displayCloseButton,
offering: nil,
paywallResultHandler: paywallResultHandler)
}

@objc
public func presentPaywall(offering: Offering, paywallResultHandler: @escaping (String) -> Void) {
self.privatePresentPaywall(displayCloseButton: nil,
offering: offering,
self.privatePresentPaywall(content: .offering(offering),
paywallResultHandler: paywallResultHandler)
}

Expand All @@ -84,59 +71,57 @@ import UIKit
displayCloseButton: Bool,
paywallResultHandler: @escaping (String) -> Void) {
self.privatePresentPaywall(displayCloseButton: displayCloseButton,
offering: offering,
content: .offering(offering),
paywallResultHandler: paywallResultHandler)
}

@available(*, deprecated, message: "Use presentPaywallIfNeeded with paywallResultHandler instead")
@objc
public func presentPaywallIfNeeded(requiredEntitlementIdentifier: String) {
self.privatePresentPaywallIfNeeded(requiredEntitlementIdentifier: requiredEntitlementIdentifier,
displayCloseButton: nil,
offering: nil,
paywallResultHandler: nil)
public func presentPaywall(offeringIdentifier: String,
displayCloseButton: Bool,
paywallResultHandler: @escaping (String) -> Void) {
self.privatePresentPaywall(displayCloseButton: displayCloseButton,
content: .offeringIdentifier(offeringIdentifier),
paywallResultHandler: paywallResultHandler)
}

@available(*, deprecated, message: "Use presentPaywallIfNeeded with paywallResultHandler instead")
@objc
public func presentPaywallIfNeeded(requiredEntitlementIdentifier: String,
displayCloseButton: Bool) {
public func presentPaywallIfNeeded(requiredEntitlementIdentifier: String,
paywallResultHandler: @escaping (String) -> Void) {
self.privatePresentPaywallIfNeeded(requiredEntitlementIdentifier: requiredEntitlementIdentifier,
displayCloseButton: displayCloseButton,
offering: nil,
paywallResultHandler: nil)
paywallResultHandler: paywallResultHandler)
}

@objc
public func presentPaywallIfNeeded(requiredEntitlementIdentifier: String,
displayCloseButton: Bool,
paywallResultHandler: @escaping (String) -> Void) {
self.privatePresentPaywallIfNeeded(requiredEntitlementIdentifier: requiredEntitlementIdentifier,
displayCloseButton: nil,
offering: nil,
displayCloseButton: displayCloseButton,
paywallResultHandler: paywallResultHandler)
}

@objc
public func presentPaywallIfNeeded(requiredEntitlementIdentifier: String,
offeringIdentifier: String,
displayCloseButton: Bool,
paywallResultHandler: @escaping (String) -> Void) {
self.privatePresentPaywallIfNeeded(requiredEntitlementIdentifier: requiredEntitlementIdentifier,
displayCloseButton: displayCloseButton,
offering: nil,
content: .offeringIdentifier(offeringIdentifier),
paywallResultHandler: paywallResultHandler)
}

private func privatePresentPaywallIfNeeded(requiredEntitlementIdentifier: String,
displayCloseButton: Bool?,
offering: Offering?,
displayCloseButton: Bool = false,
content: Content = .defaultOffering,
paywallResultHandler: ((String) -> Void)? = nil) {
_ = Task { @MainActor in
do {
let customerInfo = try await Purchases.shared.customerInfo()
let shouldDisplay = !customerInfo.entitlements.active.keys.contains(requiredEntitlementIdentifier)
if shouldDisplay {
self.privatePresentPaywall(displayCloseButton: displayCloseButton,
offering: offering,
content: content,
paywallResultHandler: paywallResultHandler)
} else {
paywallResultHandler?(PaywallResult.notPresented.name)
Expand All @@ -147,26 +132,31 @@ import UIKit
}
}

private func privatePresentPaywall(displayCloseButton: Bool?,
offering: Offering?,
private func privatePresentPaywall(displayCloseButton: Bool = false,
content: Content = .defaultOffering,
paywallResultHandler: ((String) -> Void)? = nil) {
guard let rootController = Self.rootViewController else {
NSLog("Unable to find root UIViewController")
return
}

let controller: PaywallViewController
if let displayCloseButton = displayCloseButton {
switch content {
case let .offering(offering):
controller = PaywallViewController(offering: offering, displayCloseButton: displayCloseButton)
} else {
controller = PaywallViewController()
case let .offeringIdentifier(identifier):
controller = PaywallViewController(offeringIdentifier: identifier, displayCloseButton: displayCloseButton)
case .defaultOffering:
controller = PaywallViewController(displayCloseButton: displayCloseButton)
}

controller.delegate = self
controller.modalPresentationStyle = .pageSheet

if let paywallResultHandler {
self.resultByVC[controller] = (paywallResultHandler, .cancelled)
}

rootController.present(controller, animated: true)
}

Expand All @@ -180,6 +170,14 @@ import UIKit
return windowScene.windows.first?.rootViewController
}

private enum Content {

case offering(Offering)
case offeringIdentifier(String)
case defaultOffering

}

}

@available(iOS 15.0, *)
Expand Down Expand Up @@ -216,4 +214,51 @@ extension PaywallProxy: PaywallViewControllerDelegate {

}

// MARK: - Deprecations

@available(iOS 15.0, *)
extension PaywallProxy {

@available(*, deprecated, message: "Use presentPaywall with paywallResultHandler instead")
@objc
public func presentPaywall() {
self.privatePresentPaywall()
}

@available(*, deprecated, message: "Use presentPaywall with paywallResultHandler instead")
@objc
public func presentPaywall(displayCloseButton: Bool) {
self.privatePresentPaywall(displayCloseButton: displayCloseButton)
}

@available(*, deprecated, message: "Use presentPaywall with paywallResultHandler instead")
@objc
public func presentPaywall(offering: Offering) {
self.privatePresentPaywall(content: .offering(offering))
}

@available(*, deprecated, message: "Use presentPaywall with paywallResultHandler instead")
@objc
public func presentPaywall(offering: Offering, displayCloseButton: Bool) {
self.privatePresentPaywall(displayCloseButton: displayCloseButton, content: .offering(offering))
}

@available(*, deprecated, message: "Use presentPaywallIfNeeded with paywallResultHandler instead")
@objc
public func presentPaywallIfNeeded(requiredEntitlementIdentifier: String) {
self.privatePresentPaywallIfNeeded(requiredEntitlementIdentifier: requiredEntitlementIdentifier,
paywallResultHandler: nil)
}

@available(*, deprecated, message: "Use presentPaywallIfNeeded with paywallResultHandler instead")
@objc
public func presentPaywallIfNeeded(requiredEntitlementIdentifier: String,
displayCloseButton: Bool) {
self.privatePresentPaywallIfNeeded(requiredEntitlementIdentifier: requiredEntitlementIdentifier,
displayCloseButton: displayCloseButton,
paywallResultHandler: nil)
}

}

#endif

0 comments on commit ffa8bad

Please sign in to comment.