From e12189e64095dff07f527a10b5e9a93fa3502645 Mon Sep 17 00:00:00 2001 From: Engin Kurutepe Date: Wed, 8 Jan 2025 22:50:40 +0100 Subject: [PATCH 1/3] add contact support button to customer center --- RevenueCat.xcodeproj/project.pbxproj | 4 ++ .../CustomerCenter/URLUtilities.swift | 4 ++ .../ViewModels/CustomerCenterViewModel.swift | 2 +- .../ManageSubscriptionsViewModel.swift | 2 + .../Views/ContactSupportButton.swift | 52 +++++++++++++++++++ .../Views/ManageSubscriptionsView.swift | 7 ++- .../Views/NoSubscriptionsView.swift | 4 ++ .../Views/WrongPlatformView.swift | 20 ++----- 8 files changed, 76 insertions(+), 19 deletions(-) create mode 100644 RevenueCatUI/CustomerCenter/Views/ContactSupportButton.swift diff --git a/RevenueCat.xcodeproj/project.pbxproj b/RevenueCat.xcodeproj/project.pbxproj index b5eb658028..3af483bb2f 100644 --- a/RevenueCat.xcodeproj/project.pbxproj +++ b/RevenueCat.xcodeproj/project.pbxproj @@ -764,6 +764,7 @@ 77BA1AB32CCBB6EE009BF0EA /* RootView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 77BA1AB22CCBB6EE009BF0EA /* RootView.swift */; }; 805B60C97993B311CEC93EAF /* ProductsFetcherSK2.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3628C1F100BB3C1782860D24 /* ProductsFetcherSK2.swift */; }; 80E80EF226970E04008F245A /* ReceiptFetcher.swift in Sources */ = {isa = PBXBuildFile; fileRef = 80E80EF026970DC3008F245A /* ReceiptFetcher.swift */; }; + 875481962D2F1EB100D5CEAD /* ContactSupportButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 875481952D2F1EB100D5CEAD /* ContactSupportButton.swift */; }; 8834AFA52C2B9375005A72FE /* PresentIfNeededTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 887A62222C1D168B00E1A461 /* PresentIfNeededTests.swift */; }; 887A5FBD2C1D036200E1A461 /* RevenueCatUIDev.h in Headers */ = {isa = PBXBuildFile; fileRef = 887A5FBC2C1D036200E1A461 /* RevenueCatUIDev.h */; settings = {ATTRIBUTES = (Public, ); }; }; 887A60672C1D037000E1A461 /* PaywallError.swift in Sources */ = {isa = PBXBuildFile; fileRef = 887A5FC72C1D037000E1A461 /* PaywallError.swift */; }; @@ -2016,6 +2017,7 @@ 77BA1AB22CCBB6EE009BF0EA /* RootView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RootView.swift; sourceTree = ""; }; 80E80EF026970DC3008F245A /* ReceiptFetcher.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ReceiptFetcher.swift; sourceTree = ""; }; 84C3F1AC1D7E1E64341D3936 /* Pods_RevenueCat_PurchasesTests.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_RevenueCat_PurchasesTests.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + 875481952D2F1EB100D5CEAD /* ContactSupportButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContactSupportButton.swift; sourceTree = ""; }; 887A5FB42C1D024300E1A461 /* Package.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Package.swift; sourceTree = ""; }; 887A5FBA2C1D036200E1A461 /* RevenueCatUI.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = RevenueCatUI.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 887A5FBC2C1D036200E1A461 /* RevenueCatUIDev.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = RevenueCatUIDev.h; sourceTree = ""; }; @@ -3604,6 +3606,7 @@ C3AD12BB2C6EA69D00A4F86F /* SubscriptionDetailsView.swift */, 77372D982C6F8C7B008E59D3 /* AppUpdateWarningView.swift */, 352304892D0A0F2B003971A4 /* ErrorView.swift */, + 875481952D2F1EB100D5CEAD /* ContactSupportButton.swift */, ); path = Views; sourceTree = ""; @@ -6580,6 +6583,7 @@ 887A60C02C1D037000E1A461 /* AsyncButton.swift in Sources */, 887A60892C1D037000E1A461 /* PaywallPurchasesType.swift in Sources */, 2C8EC71B2CCDD43900D6CCF8 /* ComponentViewState.swift in Sources */, + 875481962D2F1EB100D5CEAD /* ContactSupportButton.swift in Sources */, 77089F9E2CD39EC100848CD5 /* ShadowModifier.swift in Sources */, 3537566F2C382C2800A1B8D6 /* WrongPlatformView.swift in Sources */, 887A60C22C1D037000E1A461 /* ErrorDisplay.swift in Sources */, diff --git a/RevenueCatUI/CustomerCenter/URLUtilities.swift b/RevenueCatUI/CustomerCenter/URLUtilities.swift index 6fb79a2da4..bb3c5ce2cc 100644 --- a/RevenueCatUI/CustomerCenter/URLUtilities.swift +++ b/RevenueCatUI/CustomerCenter/URLUtilities.swift @@ -65,11 +65,15 @@ enum URLUtilities { } static func canOpenURL(_ url: URL) -> Bool { +#if DEBUG + return true +#else guard !Self.isAppExtension, let application = Self.sharedUIApplication else { return false } return application.canOpenURL(url) +#endif } } diff --git a/RevenueCatUI/CustomerCenter/ViewModels/CustomerCenterViewModel.swift b/RevenueCatUI/CustomerCenter/ViewModels/CustomerCenterViewModel.swift index 069cc09e73..8da8a5ec66 100644 --- a/RevenueCatUI/CustomerCenter/ViewModels/CustomerCenterViewModel.swift +++ b/RevenueCatUI/CustomerCenter/ViewModels/CustomerCenterViewModel.swift @@ -93,7 +93,7 @@ import RevenueCat #if DEBUG convenience init( - purchaseInformation: PurchaseInformation, + purchaseInformation: PurchaseInformation?, configuration: CustomerCenterConfigData ) { self.init(customerCenterActionHandler: nil) diff --git a/RevenueCatUI/CustomerCenter/ViewModels/ManageSubscriptionsViewModel.swift b/RevenueCatUI/CustomerCenter/ViewModels/ManageSubscriptionsViewModel.swift index a0cc590c9f..81992fb1b3 100644 --- a/RevenueCatUI/CustomerCenter/ViewModels/ManageSubscriptionsViewModel.swift +++ b/RevenueCatUI/CustomerCenter/ViewModels/ManageSubscriptionsViewModel.swift @@ -36,6 +36,8 @@ class ManageSubscriptionsViewModel: ObservableObject { @Published var loadingPath: CustomerCenterConfigData.HelpPath? @Published + private(set) var supportInformation: CustomerCenterConfigData.Support? + @Published var promotionalOfferData: PromotionalOfferData? @Published var inAppBrowserURL: IdentifiableURL? diff --git a/RevenueCatUI/CustomerCenter/Views/ContactSupportButton.swift b/RevenueCatUI/CustomerCenter/Views/ContactSupportButton.swift new file mode 100644 index 0000000000..6ce7b341df --- /dev/null +++ b/RevenueCatUI/CustomerCenter/Views/ContactSupportButton.swift @@ -0,0 +1,52 @@ +// +// 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 +// +// ContactSupportButton.swift +// +// Created by Engin Kurutepe on 08.01.25. + +import SwiftUI +import RevenueCat + +#if os(iOS) + +@available(iOS 15.0, macOS 12.0, tvOS 15.0, watchOS 8.0, *) +@available(macOS, unavailable) +@available(tvOS, unavailable) +@available(watchOS, unavailable) +struct ContactSupportButton: View { + @Environment(\.openURL) + private var openURL + @Environment(\.localization) + private var localization: CustomerCenterConfigData.Localization + let support: CustomerCenterConfigData.Support? + + var body: some View { + if let supportURL { + Button(localization.commonLocalizedString(for: .contactSupport)) { + Task { + openURL(supportURL) + } + } + } else { + EmptyView() + } + } + + private var supportURL: URL? { + guard let support else { return nil } + let subject = self.localization.commonLocalizedString(for: .defaultSubject) + let body = support.calculateBody(self.localization) + return URLUtilities.createMailURLIfPossible(email: support.email, + subject: subject, + body: body) + } +} + +#endif diff --git a/RevenueCatUI/CustomerCenter/Views/ManageSubscriptionsView.swift b/RevenueCatUI/CustomerCenter/Views/ManageSubscriptionsView.swift index 12e4690623..533d3c8ce1 100644 --- a/RevenueCatUI/CustomerCenter/Views/ManageSubscriptionsView.swift +++ b/RevenueCatUI/CustomerCenter/Views/ManageSubscriptionsView.swift @@ -30,6 +30,8 @@ struct ManageSubscriptionsView: View { private var localization: CustomerCenterConfigData.Localization @Environment(\.colorScheme) private var colorScheme + @Environment(\.supportInformation) + private var supportInformation @StateObject private var viewModel: ManageSubscriptionsViewModel @@ -81,7 +83,6 @@ struct ManageSubscriptionsView: View { var content: some View { ZStack { List { - if let purchaseInformation = self.viewModel.purchaseInformation { Section { SubscriptionDetailsView( @@ -91,6 +92,8 @@ struct ManageSubscriptionsView: View { Section { ManageSubscriptionsButtonsView(viewModel: self.viewModel, loadingPath: self.$viewModel.loadingPath) + + ContactSupportButton(support: supportInformation) } header: { if let subtitle = self.viewModel.screen.subtitle { Text(subtitle) @@ -170,6 +173,7 @@ struct ManageSubscriptionsView_Previews: PreviewProvider { customerCenterActionHandler: nil) .environment(\.localization, CustomerCenterConfigTestData.customerCenterData.localization) .environment(\.appearance, CustomerCenterConfigTestData.customerCenterData.appearance) + .environment(\.supportInformation, CustomerCenterConfigTestData.customerCenterData.support) }.preferredColorScheme(colorScheme) .previewDisplayName("Monthly renewing - \(colorScheme)") @@ -182,6 +186,7 @@ struct ManageSubscriptionsView_Previews: PreviewProvider { customerCenterActionHandler: nil) .environment(\.localization, CustomerCenterConfigTestData.customerCenterData.localization) .environment(\.appearance, CustomerCenterConfigTestData.customerCenterData.appearance) + .environment(\.supportInformation, CustomerCenterConfigTestData.customerCenterData.support) }.preferredColorScheme(colorScheme) .previewDisplayName("Yearly expiring - \(colorScheme)") } diff --git a/RevenueCatUI/CustomerCenter/Views/NoSubscriptionsView.swift b/RevenueCatUI/CustomerCenter/Views/NoSubscriptionsView.swift index b6fddbc91c..5d2122ed73 100644 --- a/RevenueCatUI/CustomerCenter/Views/NoSubscriptionsView.swift +++ b/RevenueCatUI/CustomerCenter/Views/NoSubscriptionsView.swift @@ -32,6 +32,8 @@ struct NoSubscriptionsView: View { private var appearance: CustomerCenterConfigData.Appearance @Environment(\.colorScheme) private var colorScheme + @Environment(\.openURL) + var openURL @State private var showRestoreAlert: Bool = false @@ -58,6 +60,8 @@ struct NoSubscriptionsView: View { showRestoreAlert = true } .restorePurchasesAlert(isPresented: $showRestoreAlert) + + ContactSupportButton(support: configuration.support) } } diff --git a/RevenueCatUI/CustomerCenter/Views/WrongPlatformView.swift b/RevenueCatUI/CustomerCenter/Views/WrongPlatformView.swift index 093c84e5df..20c8a66463 100644 --- a/RevenueCatUI/CustomerCenter/Views/WrongPlatformView.swift +++ b/RevenueCatUI/CustomerCenter/Views/WrongPlatformView.swift @@ -48,15 +48,6 @@ struct WrongPlatformView: View { @Environment(\.openURL) private var openURL - private var supportURL: URL? { - guard let supportInformation = self.supportInformation else { return nil } - let subject = self.localization.commonLocalizedString(for: .defaultSubject) - let body = supportInformation.calculateBody(self.localization) - return URLUtilities.createMailURLIfPossible(email: supportInformation.email, - subject: subject, - body: body) - } - init(screen: CustomerCenterConfigData.Screen? = nil, purchaseInformation: PurchaseInformation) { self.screen = screen @@ -88,14 +79,9 @@ struct WrongPlatformView: View { } } } - if let url = supportURL { - Section { - AsyncButton { - openURL(url) - } label: { - Text(localization.commonLocalizedString(for: .contactSupport)) - } - } + + Section { + ContactSupportButton(support: supportInformation) } } .toolbar { From 76cd2c70ac3b60f2acc549e32368c1a608878a5c Mon Sep 17 00:00:00 2001 From: Engin Kurutepe Date: Wed, 8 Jan 2025 22:56:26 +0100 Subject: [PATCH 2/3] remove unused openURL --- RevenueCatUI/CustomerCenter/Views/NoSubscriptionsView.swift | 2 -- 1 file changed, 2 deletions(-) diff --git a/RevenueCatUI/CustomerCenter/Views/NoSubscriptionsView.swift b/RevenueCatUI/CustomerCenter/Views/NoSubscriptionsView.swift index 5d2122ed73..60c8e43b7c 100644 --- a/RevenueCatUI/CustomerCenter/Views/NoSubscriptionsView.swift +++ b/RevenueCatUI/CustomerCenter/Views/NoSubscriptionsView.swift @@ -32,8 +32,6 @@ struct NoSubscriptionsView: View { private var appearance: CustomerCenterConfigData.Appearance @Environment(\.colorScheme) private var colorScheme - @Environment(\.openURL) - var openURL @State private var showRestoreAlert: Bool = false From 301f603ddfb89066f3f63be6df6c014ee261cf7b Mon Sep 17 00:00:00 2001 From: Engin Kurutepe Date: Wed, 8 Jan 2025 23:31:17 +0100 Subject: [PATCH 3/3] add latest macOS to bundler platforms --- Gemfile.lock | 1 + 1 file changed, 1 insertion(+) diff --git a/Gemfile.lock b/Gemfile.lock index f06690193c..f0e48b05ef 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -364,6 +364,7 @@ PLATFORMS arm64-darwin-21 arm64-darwin-22 arm64-darwin-23 + arm64-darwin-24 x86_64-darwin-22 x86_64-linux