From e12b0f6b67eedf84aa9be212f27203fb24223375 Mon Sep 17 00:00:00 2001 From: Andy Boedo Date: Tue, 17 Sep 2024 12:34:13 -0300 Subject: [PATCH 1/2] created PoC code to handle deeplinks nicely --- RevenueCat.xcodeproj/project.pbxproj | 4 + .../Views/RevenueCatDeeplinkHandler.swift | 107 ++++++++++++++++++ .../PaywallsTester/PaywallsTester/Info.plist | 13 +++ .../PaywallsTester/Products.storekit | 27 ++++- .../UI/Views/AppContentView.swift | 1 + 5 files changed, 148 insertions(+), 4 deletions(-) create mode 100644 RevenueCatUI/Views/RevenueCatDeeplinkHandler.swift diff --git a/RevenueCat.xcodeproj/project.pbxproj b/RevenueCat.xcodeproj/project.pbxproj index ec1edab20f..5aab592445 100644 --- a/RevenueCat.xcodeproj/project.pbxproj +++ b/RevenueCat.xcodeproj/project.pbxproj @@ -44,6 +44,7 @@ 2D4D6AF624F7193700B656BE /* verifyReceiptSample1.txt in Resources */ = {isa = PBXBuildFile; fileRef = 2DDE559A24C8B5E300DCB087 /* verifyReceiptSample1.txt */; }; 2D4D6AF724F7193700B656BE /* base64encodedreceiptsample1.txt in Resources */ = {isa = PBXBuildFile; fileRef = 2DDE559B24C8B5E300DCB087 /* base64encodedreceiptsample1.txt */; }; 2D4E926526990AB1000E10B0 /* StoreKit1Wrapper.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2D4E926426990AB1000E10B0 /* StoreKit1Wrapper.swift */; }; + 2D6AA7B22C99D11A001DD27A /* RevenueCatDeeplinkHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2D6AA7B12C99D11A001DD27A /* RevenueCatDeeplinkHandler.swift */; }; 2D735F7E26EFF198004E82A7 /* UnitTestsConfiguration.storekit in Resources */ = {isa = PBXBuildFile; fileRef = 2D43017726EBFD7100BAB891 /* UnitTestsConfiguration.storekit */; }; 2D803F6326F144830069D717 /* Nimble in Frameworks */ = {isa = PBXBuildFile; productRef = 2D803F6226F144830069D717 /* Nimble */; }; 2D803F6626F144BF0069D717 /* Nimble in Frameworks */ = {isa = PBXBuildFile; productRef = 2D803F6526F144BF0069D717 /* Nimble */; }; @@ -1168,6 +1169,7 @@ 2D4E926426990AB1000E10B0 /* StoreKit1Wrapper.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StoreKit1Wrapper.swift; sourceTree = ""; }; 2D5BB46A24C8E8ED00E27537 /* PurchasesReceiptParser.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PurchasesReceiptParser.swift; sourceTree = ""; }; 2D69384426DFF93300FCDBC0 /* StoreProductTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StoreProductTests.swift; sourceTree = ""; }; + 2D6AA7B12C99D11A001DD27A /* RevenueCatDeeplinkHandler.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RevenueCatDeeplinkHandler.swift; sourceTree = ""; }; 2D84458826B9CD270033B5A3 /* ReceiptFetcherTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ReceiptFetcherTests.swift; sourceTree = ""; }; 2D8D03B42799A2B90044C2ED /* DocCDocumentation.docc */ = {isa = PBXFileReference; lastKnownFileType = folder.documentationcatalog; path = DocCDocumentation.docc; sourceTree = ""; }; 2D8DB34A24072AAE00BE3D31 /* SubscriberAttributeTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SubscriberAttributeTests.swift; sourceTree = ""; }; @@ -4083,6 +4085,7 @@ 887A605D2C1D037000E1A461 /* RemoteImage.swift */, 887A605E2C1D037000E1A461 /* TemplateBackgroundImageView.swift */, 88A543E62C37A4C40039C6A5 /* TierSelectorView.swift */, + 2D6AA7B12C99D11A001DD27A /* RevenueCatDeeplinkHandler.swift */, ); path = Views; sourceTree = ""; @@ -5974,6 +5977,7 @@ 353756682C382C2800A1B8D6 /* CustomerCenterViewModel.swift in Sources */, 887A60BD2C1D037000E1A461 /* TemplateViewType.swift in Sources */, 887A606D2C1D037000E1A461 /* Localization.swift in Sources */, + 2D6AA7B22C99D11A001DD27A /* RevenueCatDeeplinkHandler.swift in Sources */, 887A60CA2C1D037000E1A461 /* RemoteImage.swift in Sources */, 88B1BAF42C813A3C001B7EE5 /* SpacerComponentViewModel.swift in Sources */, 887A607B2C1D037000E1A461 /* Bundle+Extensions.swift in Sources */, diff --git a/RevenueCatUI/Views/RevenueCatDeeplinkHandler.swift b/RevenueCatUI/Views/RevenueCatDeeplinkHandler.swift new file mode 100644 index 0000000000..bed58dcc92 --- /dev/null +++ b/RevenueCatUI/Views/RevenueCatDeeplinkHandler.swift @@ -0,0 +1,107 @@ +// +// 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 +// +// RevenueCatDeeplinkHandler.swift +// +// Created by Andrés Boedo on 9/17/24. + +import Foundation +import RevenueCat +import SwiftUI + +@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") +struct RevenueCatDeeplinkHandlerView: View { + @State private var isShowingPaywall: Bool = false + @State private var offeringID: String? + + let content: Content + + init(@ViewBuilder content: () -> Content) { + self.content = content() + } + + var body: some View { + content + .onOpenURL { url in + if let extractedOfferingID = extractOfferingID(from: url) { + offeringID = extractedOfferingID + isShowingPaywall = true + } + } + .sheet( + isPresented: $isShowingPaywall, + onDismiss: { + offeringID = nil + }, + content: { + if let offeringID = offeringID { + OfferingLoaderView(offeringID: offeringID) + } else { + Text("Invalid offering ID.") + } + }) + } + + private func extractOfferingID(from url: URL) -> String? { + let components = URLComponents(url: url, resolvingAgainstBaseURL: false) + return components?.queryItems?.first(where: { $0.name == "offeringID" })?.value + } +} + +@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 { + func handleRevenueCatDeeplinks() -> some View { + RevenueCatDeeplinkHandlerView { + self + } + } +} + +@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") +struct OfferingLoaderView: View { + let offeringID: String + @State private var offering: Offering? + @State private var isLoading = true + + var body: some View { + Group { + if let offering = offering { + PaywallView(offering: offering) + } else if isLoading { + ProgressView() + } else { + Text("Could not load offering.") + } + } + .task { + await fetchOffering() + } + } + + private func fetchOffering() async { + do { + let offerings = try await Purchases.shared.offerings() + if let offering = offerings.offering(identifier: offeringID) { + self.offering = offering + } else { + isLoading = false + print("Offering with ID \(offeringID) not found.") + } + } catch { + isLoading = false + print("Error fetching offering: \(error.localizedDescription)") + } + } +} diff --git a/Tests/TestingApps/PaywallsTester/PaywallsTester/Info.plist b/Tests/TestingApps/PaywallsTester/PaywallsTester/Info.plist index 2d4540095c..cfff3c0b3b 100644 --- a/Tests/TestingApps/PaywallsTester/PaywallsTester/Info.plist +++ b/Tests/TestingApps/PaywallsTester/PaywallsTester/Info.plist @@ -2,6 +2,19 @@ + CFBundleURLTypes + + + CFBundleTypeRole + Editor + CFBundleURLName + com.revenuecat.PaywallsTester + CFBundleURLSchemes + + paywallsTester + + + ITSAppUsesNonExemptEncryption NSAppTransportSecurity diff --git a/Tests/TestingApps/PaywallsTester/PaywallsTester/Products.storekit b/Tests/TestingApps/PaywallsTester/PaywallsTester/Products.storekit index 455eeeb763..f145953159 100644 --- a/Tests/TestingApps/PaywallsTester/PaywallsTester/Products.storekit +++ b/Tests/TestingApps/PaywallsTester/PaywallsTester/Products.storekit @@ -1,4 +1,14 @@ { + "appPolicies" : { + "eula" : "", + "policies" : [ + { + "locale" : "en_US", + "policyText" : "", + "policyURL" : "" + } + ] + }, "identifier" : "882C6E98", "nonRenewingSubscriptions" : [ @@ -132,7 +142,10 @@ "recurringSubscriptionPeriod" : "P1M", "referenceName" : "Monthly", "subscriptionGroupID" : "CEEF018E", - "type" : "RecurringSubscription" + "type" : "RecurringSubscription", + "winbackOffers" : [ + + ] }, { "adHocOffers" : [ @@ -167,7 +180,10 @@ "recurringSubscriptionPeriod" : "P1Y", "referenceName" : "Yearly", "subscriptionGroupID" : "CEEF018E", - "type" : "RecurringSubscription" + "type" : "RecurringSubscription", + "winbackOffers" : [ + + ] }, { "adHocOffers" : [ @@ -192,13 +208,16 @@ "recurringSubscriptionPeriod" : "P1W", "referenceName" : "Weekly", "subscriptionGroupID" : "CEEF018E", - "type" : "RecurringSubscription" + "type" : "RecurringSubscription", + "winbackOffers" : [ + + ] } ] } ], "version" : { - "major" : 3, + "major" : 4, "minor" : 0 } } diff --git a/Tests/TestingApps/PaywallsTester/PaywallsTester/UI/Views/AppContentView.swift b/Tests/TestingApps/PaywallsTester/PaywallsTester/UI/Views/AppContentView.swift index 5028c0891d..e6d5d938ad 100644 --- a/Tests/TestingApps/PaywallsTester/PaywallsTester/UI/Views/AppContentView.swift +++ b/Tests/TestingApps/PaywallsTester/PaywallsTester/UI/Views/AppContentView.swift @@ -44,6 +44,7 @@ struct AppContentView: View { } #endif } + .handleRevenueCatDeeplinks() } private var background: some View { From 749cfc59cc7a81b5884829bd000bb1173e46b1fb Mon Sep 17 00:00:00 2001 From: Andy Boedo Date: Tue, 17 Sep 2024 12:45:55 -0300 Subject: [PATCH 2/2] revert unrelated changes --- .../PaywallsTester/Products.storekit | 27 +++---------------- 1 file changed, 4 insertions(+), 23 deletions(-) diff --git a/Tests/TestingApps/PaywallsTester/PaywallsTester/Products.storekit b/Tests/TestingApps/PaywallsTester/PaywallsTester/Products.storekit index f145953159..455eeeb763 100644 --- a/Tests/TestingApps/PaywallsTester/PaywallsTester/Products.storekit +++ b/Tests/TestingApps/PaywallsTester/PaywallsTester/Products.storekit @@ -1,14 +1,4 @@ { - "appPolicies" : { - "eula" : "", - "policies" : [ - { - "locale" : "en_US", - "policyText" : "", - "policyURL" : "" - } - ] - }, "identifier" : "882C6E98", "nonRenewingSubscriptions" : [ @@ -142,10 +132,7 @@ "recurringSubscriptionPeriod" : "P1M", "referenceName" : "Monthly", "subscriptionGroupID" : "CEEF018E", - "type" : "RecurringSubscription", - "winbackOffers" : [ - - ] + "type" : "RecurringSubscription" }, { "adHocOffers" : [ @@ -180,10 +167,7 @@ "recurringSubscriptionPeriod" : "P1Y", "referenceName" : "Yearly", "subscriptionGroupID" : "CEEF018E", - "type" : "RecurringSubscription", - "winbackOffers" : [ - - ] + "type" : "RecurringSubscription" }, { "adHocOffers" : [ @@ -208,16 +192,13 @@ "recurringSubscriptionPeriod" : "P1W", "referenceName" : "Weekly", "subscriptionGroupID" : "CEEF018E", - "type" : "RecurringSubscription", - "winbackOffers" : [ - - ] + "type" : "RecurringSubscription" } ] } ], "version" : { - "major" : 4, + "major" : 3, "minor" : 0 } }