Skip to content

Commit

Permalink
New DebugViewController: UIKit counterpart for SwiftUI's `debugReve…
Browse files Browse the repository at this point in the history
…nueCatOverlay` (#2631)

Built on top of `DebugSwiftUIRootView`. This can be presented either
manually, or using `UIViewController.presentDebugRevenueCatOverlay`.

This looks exactly the same as the `SwiftUI` version:

![image](https://github.com/RevenueCat/purchases-ios/assets/685609/2ef22db4-0030-457e-a721-8daf2dcc1a49)
  • Loading branch information
NachoSoto authored Jun 13, 2023
1 parent 42d1aae commit 5ec9d36
Show file tree
Hide file tree
Showing 9 changed files with 170 additions and 9 deletions.
4 changes: 4 additions & 0 deletions RevenueCat.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -241,6 +241,7 @@
4FA696BD2A0020A000D228B1 /* MainThreadMonitor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4FA696BC2A0020A000D228B1 /* MainThreadMonitor.swift */; };
4FCBA84F2A15391B004134BD /* SnapshotTesting+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 576C8A9127D27DDD0058FA6E /* SnapshotTesting+Extensions.swift */; };
4FCBA8512A153940004134BD /* SnapshotTesting in Frameworks */ = {isa = PBXBuildFile; productRef = 4FCBA8502A153940004134BD /* SnapshotTesting */; };
4FCEEA5E2A379B80002C2112 /* DebugViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4FCEEA5D2A379B80002C2112 /* DebugViewController.swift */; };
4FCEEA612A379CF9002C2112 /* DebugViewSwiftUITests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4FCEEA602A379CF9002C2112 /* DebugViewSwiftUITests.swift */; };
4FCEEA632A37A2E9002C2112 /* ImageSnapshot.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4FCEEA622A37A2E9002C2112 /* ImageSnapshot.swift */; };
4FD291BE2A1E9A2E0098D1B9 /* StoreKit2TransactionFetcherTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4FD291BD2A1E9A2E0098D1B9 /* StoreKit2TransactionFetcherTests.swift */; };
Expand Down Expand Up @@ -934,6 +935,7 @@
4FA696A329FC43C600D228B1 /* ReceiptParserTests-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = "ReceiptParserTests-Info.plist"; sourceTree = "<group>"; };
4FA696BC2A0020A000D228B1 /* MainThreadMonitor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MainThreadMonitor.swift; sourceTree = "<group>"; };
4FCBA8522A1539D0004134BD /* __Snapshots__ */ = {isa = PBXFileReference; lastKnownFileType = folder; path = __Snapshots__; sourceTree = "<group>"; };
4FCEEA5D2A379B80002C2112 /* DebugViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DebugViewController.swift; sourceTree = "<group>"; };
4FCEEA602A379CF9002C2112 /* DebugViewSwiftUITests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DebugViewSwiftUITests.swift; sourceTree = "<group>"; };
4FCEEA622A37A2E9002C2112 /* ImageSnapshot.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ImageSnapshot.swift; sourceTree = "<group>"; };
4FD291BD2A1E9A2E0098D1B9 /* StoreKit2TransactionFetcherTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StoreKit2TransactionFetcherTests.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -2115,6 +2117,7 @@
4F6BEDD82A26B55C00CD9322 /* DebugViewModel.swift */,
4F6BEDDF2A26B65900CD9322 /* DebugViewSheetPresentation.swift */,
4F6BEDE12A26B69500CD9322 /* DebugContentViews.swift */,
4FCEEA5D2A379B80002C2112 /* DebugViewController.swift */,
);
path = DebugUI;
sourceTree = "<group>";
Expand Down Expand Up @@ -3202,6 +3205,7 @@
B3B5FBBF269E081E00104A0C /* InMemoryCachedObject.swift in Sources */,
579B67F428C5326A0094F7E8 /* PaymentQueueWrapper.swift in Sources */,
B3DDB55926854865008CCF23 /* PurchaseOwnershipType.swift in Sources */,
4FCEEA5E2A379B80002C2112 /* DebugViewController.swift in Sources */,
579415D529368AB200218FBC /* ReceiptStrings.swift in Sources */,
57A0FBF02749C0C2009E2FC3 /* Atomic.swift in Sources */,
2DC5623224EC63730031F69B /* TransactionsFactory.swift in Sources */,
Expand Down
2 changes: 2 additions & 0 deletions Sources/Support/DebugUI/DebugContentViews.swift
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,8 @@ struct DebugSwiftUIRootView: View {
}
}

static let cornerRadius: CGFloat = 24

}

private enum DebugViewPath: Hashable {
Expand Down
2 changes: 1 addition & 1 deletion Sources/Support/DebugUI/DebugView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ public extension View {
],
isPresented: isPresented,
largestUndimmedIdentifier: .fraction(0.6),
cornerRadius: 10,
cornerRadius: DebugSwiftUIRootView.cornerRadius,
content: {
DebugSwiftUIRootView()
}
Expand Down
88 changes: 88 additions & 0 deletions Sources/Support/DebugUI/DebugViewController.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
//
// Copyright RevenueCat Inc. All Rights Reserved.
//
// Licensed under the MIT License (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://opensource.org/licenses/MIT
//
// DebugViewController.swift
//
// Created by Nacho Soto on 6/12/23.

import Foundation

#if DEBUG && os(iOS) && swift(>=5.8)

import SwiftUI
import UIKit

/// A view controller which allows debugging the current SDK setup.
///
/// You can present this yourself, or use `UIViewController.presentDebugRevenueCatOverlay`
/// for a default presentation using `UISheetPresentationController`.
///
/// - Seealso: `View.debugRevenueCatOverlay` for `SwiftUI`.
@available(iOS 16.0, *)
@objc(RCDebugViewController)
public final class DebugViewController: UIViewController {

private lazy var hostingController: UIHostingController<DebugSwiftUIRootView> = {
.init(rootView: .init())
}()

// swiftlint:disable:next missing_docs
public init() {
super.init(nibName: nil, bundle: nil)
}

required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}

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
}

}

@available(iOS 16.0, *)
extension UIViewController {

/// Presents a bottom sheet overlay on top of the current view controller
/// which allows debugging the current SDK setup.
///
/// - Seealso: `DebugViewController`.
/// - Seealso: `View.debugRevenueCatOverlay` for `SwiftUI`.
@objc(rc_presentDebugRevenueCatOverlayAnimated:)
public func presentDebugRevenueCatOverlay(animated: Bool = true) {
let controller = DebugViewController()

if let sheet = controller.sheetPresentationController {
sheet.detents = [
.custom(resolver: { context in context.maximumDetentValue * 0.2 }),
.medium(),
.large()
]
sheet.largestUndimmedDetentIdentifier = .medium
sheet.preferredCornerRadius = DebugSwiftUIRootView.cornerRadius
sheet.prefersGrabberVisible = true
}

self.present(controller, animated: animated)
}

}

#endif
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
2DD77914270E23870079CBD4 /* RCOfferingAPI.m in Sources */ = {isa = PBXBuildFile; fileRef = A5D614DF26EBE84F007DDB75 /* RCOfferingAPI.m */; };
2DD77915270E23870079CBD4 /* RCPackageAPI.m in Sources */ = {isa = PBXBuildFile; fileRef = A5D614DE26EBE84F007DDB75 /* RCPackageAPI.m */; };
2DD77916270E23870079CBD4 /* RCTransactionAPI.m in Sources */ = {isa = PBXBuildFile; fileRef = A5D614F426EBE84F007DDB75 /* RCTransactionAPI.m */; };
4F34093B2A37E5930050EA0E /* RCOtherAPI.m in Sources */ = {isa = PBXBuildFile; fileRef = 4F34093A2A37E5930050EA0E /* RCOtherAPI.m */; };
570FAF502864ECB000D3C769 /* RCNonSubscriptionTransactionAPI.m in Sources */ = {isa = PBXBuildFile; fileRef = 570FAF4F2864ECB000D3C769 /* RCNonSubscriptionTransactionAPI.m */; };
5738F428278672070096D623 /* RCStoreProductDiscountAPI.m in Sources */ = {isa = PBXBuildFile; fileRef = 5738F427278672070096D623 /* RCStoreProductDiscountAPI.m */; };
5738F42D278674710096D623 /* RCSubscriptionPeriodAPI.m in Sources */ = {isa = PBXBuildFile; fileRef = 5738F42C278674710096D623 /* RCSubscriptionPeriodAPI.m */; };
Expand Down Expand Up @@ -53,6 +54,8 @@
/* Begin PBXFileReference section */
2C396F5D281C64B700669657 /* AdServices.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AdServices.framework; path = System/Library/Frameworks/AdServices.framework; sourceTree = SDKROOT; };
2DD778F5270E235B0079CBD4 /* ObjCAPITester.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = ObjCAPITester.app; sourceTree = BUILT_PRODUCTS_DIR; };
4F3409392A37E5930050EA0E /* RCOtherAPI.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = RCOtherAPI.h; sourceTree = "<group>"; };
4F34093A2A37E5930050EA0E /* RCOtherAPI.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = RCOtherAPI.m; sourceTree = "<group>"; };
570FAF4E2864ECB000D3C769 /* RCNonSubscriptionTransactionAPI.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = RCNonSubscriptionTransactionAPI.h; sourceTree = "<group>"; };
570FAF4F2864ECB000D3C769 /* RCNonSubscriptionTransactionAPI.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = RCNonSubscriptionTransactionAPI.m; sourceTree = "<group>"; };
5738F426278672070096D623 /* RCStoreProductDiscountAPI.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = RCStoreProductDiscountAPI.h; sourceTree = "<group>"; };
Expand Down Expand Up @@ -160,6 +163,8 @@
A5D614DF26EBE84F007DDB75 /* RCOfferingAPI.m */,
A5D614F326EBE84F007DDB75 /* RCOfferingsAPI.h */,
A5D614DD26EBE84E007DDB75 /* RCOfferingsAPI.m */,
4F3409392A37E5930050EA0E /* RCOtherAPI.h */,
4F34093A2A37E5930050EA0E /* RCOtherAPI.m */,
A5D614E126EBE84F007DDB75 /* RCPackageAPI.h */,
A5D614DE26EBE84F007DDB75 /* RCPackageAPI.m */,
B3A4C835280DE95000D4AE17 /* RCPromotionalOfferAPI.h */,
Expand Down Expand Up @@ -287,6 +292,7 @@
5738F42E2786755E0096D623 /* main.m in Sources */,
B32554452825E74000DA62EA /* RCConfigurationAPI.m in Sources */,
2DD7790E270E23870079CBD4 /* RCIntroEligibilityAPI.m in Sources */,
4F34093B2A37E5930050EA0E /* RCOtherAPI.m in Sources */,
5738F42D278674710096D623 /* RCSubscriptionPeriodAPI.m in Sources */,
2DD77916270E23870079CBD4 /* RCTransactionAPI.m in Sources */,
57918A1628F4C58300BF4963 /* RCPurchasesDiagnosticsAPI.m in Sources */,
Expand Down
18 changes: 18 additions & 0 deletions Tests/APITesters/ObjCAPITester/ObjCAPITester/RCOtherAPI.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
//
// RCOtherAPI.h
// ObjCAPITester
//
// Created by Nacho Soto on 6/12/23.
//

#import <Foundation/Foundation.h>

NS_ASSUME_NONNULL_BEGIN

@interface RCOtherAPI : NSObject

+ (void)checkAPI;

@end

NS_ASSUME_NONNULL_END
25 changes: 25 additions & 0 deletions Tests/APITesters/ObjCAPITester/ObjCAPITester/RCOtherAPI.m
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
//
// RCOtherAPI.m
// ObjCAPITester
//
// Created by Nacho Soto on 6/12/23.
//

@import RevenueCat;
@import UIKit;

#import "RCOtherAPI.h"

@implementation RCOtherAPI

+ (void)checkAPI {
#if DEBUG && TARGET_OS_IPHONE && defined(__IPHONE_17_0)
if (@available(iOS 16.0, *)) {
RCDebugViewController *controller __unused = [RCDebugViewController new];

[UIViewController.new rc_presentDebugRevenueCatOverlayAnimated:NO];
}
#endif
}

@end
11 changes: 9 additions & 2 deletions Tests/APITesters/SwiftAPITester/SwiftAPITester/OtherAPI.swift
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,15 @@ struct AppView: View {

}

@available(iOS 16.0, macOS 13.0, tvOS 16.0, *)
func debugViewController() {
let _: UIViewController = DebugViewController()
UIViewController().presentDebugRevenueCatOverlay()
UIViewController().presentDebugRevenueCatOverlay(animated: false)
}

#endif

#if os(iOS) && swift(>=5.9)

@available(iOS 17.0, macOS 14.0, tvOS 17.0, watchOS 10.0, *)
Expand Down Expand Up @@ -53,5 +62,3 @@ struct PaywallViews: View {
}

#endif

#endif
Original file line number Diff line number Diff line change
Expand Up @@ -25,25 +25,36 @@ class InitialViewController: UIViewController {
print(e.localizedDescription)
}

let controllerToPresent: UIViewController

// Route the view depending if we have a premium cat user or not
if customerInfo?.entitlements["pro_cat"]?.isActive == true {

// if we have a pro_cat subscriber, send them to the cat screen
let storyboard = UIStoryboard(name: "Main", bundle: nil)
let controller = storyboard.instantiateViewController(withIdentifier: "cats")
controller.modalPresentationStyle = .fullScreen
self.present(controller, animated: true, completion: nil)

controllerToPresent = storyboard.instantiateViewController(withIdentifier: "cats")
controllerToPresent.modalPresentationStyle = .fullScreen
} else {
// if we don't have a pro subscriber, send them to the upsell screen
let controller = SwiftPaywall(
termsOfServiceUrlString: "https://www.revenuecat.com/terms",
privacyPolicyUrlString: "https://www.revenuecat.com/terms")
privacyPolicyUrlString: "https://www.revenuecat.com/terms"
)

controller.titleLabel.text = "Upsell Screen"
controller.subtitleLabel.text = "New cats, unlimited cats, personal cat insights and more!"
controller.modalPresentationStyle = .fullScreen
self.present(controller, animated: true, completion: nil)

controllerToPresent = controller
}

self.present(controllerToPresent, animated: true, completion: nil)

// Present debug screen on top
if #available(iOS 16.0, *) {
controllerToPresent.presentDebugRevenueCatOverlay(animated: true)
} else {
print("Won't display debug overlay, not supported before iOS 16.0")
}
}

Expand Down

0 comments on commit 5ec9d36

Please sign in to comment.