Skip to content

Commit

Permalink
Paywalls: UIKit PaywallViewController (#2934)
Browse files Browse the repository at this point in the history
  • Loading branch information
NachoSoto committed Sep 8, 2023
1 parent 757a529 commit dfde2a6
Show file tree
Hide file tree
Showing 5 changed files with 108 additions and 1 deletion.
2 changes: 1 addition & 1 deletion RevenueCatUI/PaywallView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ public struct PaywallView: View {
/// - Note: if `offering` does not have a current paywall, or it fails to load due to invalid data,
/// a default paywall will be displayed.
/// - Warning: `Purchases` must have been configured prior to displaying it.
public init(offering: Offering, mode: PaywallViewMode = .default) {
public init(offering: Offering?, mode: PaywallViewMode = .default) {
self.init(
offering: offering,
mode: mode,
Expand Down
76 changes: 76 additions & 0 deletions RevenueCatUI/UIKit/PaywallViewController.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
//
// PaywallViewController.swift
//
//
// Created by Nacho Soto on 8/1/23.
//

#if canImport(UIKit)

import RevenueCat
import SwiftUI
import UIKit

/// A view controller for displaying `PaywallData` for an `Offering`.
///
/// - Seealso: ``PaywallView`` for `SwiftUI`.
@available(iOS 16.0, macOS 13.0, tvOS 16.0, *)
@objc(RCPaywallViewController)
public final class PaywallViewController: UIViewController {

/// See ``PaywallViewControllerDelegate`` for receiving purchase events.
public weak var delegate: PaywallViewControllerDelegate?

private let offering: Offering?

// swiftlint:disable:next missing_docs
public init(offering: Offering? = nil) {
self.offering = offering

super.init(nibName: nil, bundle: nil)
}

// swiftlint:disable:next missing_docs
public required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}

private lazy var hostingController: UIHostingController<AnyView> = {
let view = PaywallView(offering: self.offering)
.onPurchaseCompleted { [weak self] customerInfo in
guard let self = self else { return }
self.delegate?.paywallViewController(self, didFinishPurchasingWith: customerInfo)
}

return .init(rootView: AnyView(view))
}()

public override func loadView() {
super.loadView()

self.addChild(self.hostingController)
self.view.addSubview(self.hostingController.view)
self.hostingController.didMove(toParent: self)
}

public override func viewWillLayoutSubviews() {
super.viewWillLayoutSubviews()

self.hostingController.view.frame = self.view.bounds
}

}

/// Delegate for ``PaywallViewController``.
@available(iOS 16.0, macOS 13.0, tvOS 16.0, *)
@objc(RCPaywallViewControllerDelegate)
public protocol PaywallViewControllerDelegate: AnyObject {

/// Notifies that a purchase has completed in a ``PaywallViewController``.
@objc(paywallViewController:didFinishPurchasingWithCustomerInfo:)
func paywallViewController(_ controller: PaywallViewController,
didFinishPurchasingWith customerInfo: CustomerInfo)

}

#endif
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
4F592A502A1FDC6F00851F36 /* PaywallViewAPI.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4F592A4F2A1FDC6F00851F36 /* PaywallViewAPI.swift */; };
4FD737882A61AD71002AFE75 /* RevenueCatUI in Frameworks */ = {isa = PBXBuildFile; productRef = 4FD737872A61AD71002AFE75 /* RevenueCatUI */; };
4FD7378A2A61ADF8002AFE75 /* RevenueCat in Frameworks */ = {isa = PBXBuildFile; productRef = 4FD737892A61ADF8002AFE75 /* RevenueCat */; };
57028D4E2A79325200DB1D9A /* PaywallViewControllerAPI.swift in Sources */ = {isa = PBXBuildFile; fileRef = 57028D4D2A79325200DB1D9A /* PaywallViewControllerAPI.swift */; };
5738F42127866F8F0096D623 /* main.swift in Sources */ = {isa = PBXBuildFile; fileRef = A5D614C626EBE7EA007DDB75 /* main.swift */; };
/* End PBXBuildFile section */

Expand All @@ -31,6 +32,7 @@
2C396F5B281C64AF00669657 /* AdServices.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AdServices.framework; path = System/Library/Frameworks/AdServices.framework; sourceTree = SDKROOT; };
2DD778D0270E233F0079CBD4 /* SwiftAPITester.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = SwiftAPITester.app; sourceTree = BUILT_PRODUCTS_DIR; };
4F592A4F2A1FDC6F00851F36 /* PaywallViewAPI.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PaywallViewAPI.swift; sourceTree = "<group>"; };
57028D4D2A79325200DB1D9A /* PaywallViewControllerAPI.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PaywallViewControllerAPI.swift; sourceTree = "<group>"; };
A5D614C626EBE7EA007DDB75 /* main.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = main.swift; sourceTree = "<group>"; };
/* End PBXFileReference section */

Expand Down Expand Up @@ -77,6 +79,7 @@
isa = PBXGroup;
children = (
4F592A4F2A1FDC6F00851F36 /* PaywallViewAPI.swift */,
57028D4D2A79325200DB1D9A /* PaywallViewControllerAPI.swift */,
A5D614C626EBE7EA007DDB75 /* main.swift */,
);
path = SwiftAPITester;
Expand Down Expand Up @@ -159,6 +162,7 @@
buildActionMask = 2147483647;
files = (
5738F42127866F8F0096D623 /* main.swift in Sources */,
57028D4E2A79325200DB1D9A /* PaywallViewControllerAPI.swift in Sources */,
4F592A502A1FDC6F00851F36 /* PaywallViewAPI.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ struct App: View {
var content: some View {
PaywallView()
PaywallView(mode: .fullScreen)
PaywallView(offering: nil)
PaywallView(offering: self.offering)
PaywallView(offering: self.offering, mode: .fullScreen)
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
//
// PaywallViewControllerAPI.swift
// SwiftAPITester
//
// Created by Nacho Soto on 8/1/23.
//

import RevenueCat
import RevenueCatUI
import SwiftUI

@available(iOS 16.0, macOS 13.0, tvOS 16.0, *)
func paywallViewControllerAPI(_ delegate: Delegate, _ offering: Offering?) {
let controller = PaywallViewController()
controller.delegate = delegate

let _: UIViewController = PaywallViewController(offering: offering)
}

@available(iOS 16.0, macOS 13.0, tvOS 16.0, *)
final class Delegate: PaywallViewControllerDelegate {

func paywallViewController(_ controller: PaywallViewController,
didFinishPurchasingWith customerInfo: CustomerInfo) {}

}

0 comments on commit dfde2a6

Please sign in to comment.