Skip to content

Commit

Permalink
Add ErrorView to CustomerCenter (#4574)
Browse files Browse the repository at this point in the history
  • Loading branch information
vegaro authored Dec 12, 2024
1 parent 8a07f1a commit f3ad189
Show file tree
Hide file tree
Showing 49 changed files with 217 additions and 78 deletions.
4 changes: 4 additions & 0 deletions RevenueCat.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -235,6 +235,7 @@
351B51C226D450E800BD2BD7 /* ProductRequestDataTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37E350E57B0A393455A72B40 /* ProductRequestDataTests.swift */; };
351B51C326D450F200BD2BD7 /* InMemoryCachedObjectTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37E35E3250FBBB03D92E06EC /* InMemoryCachedObjectTests.swift */; };
352137322CDBA2AA00FE961B /* FeatureEvent.swift in Sources */ = {isa = PBXBuildFile; fileRef = 352137312CDBA2A700FE961B /* FeatureEvent.swift */; };
3523048A2D0A0F30003971A4 /* ErrorView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 352304892D0A0F2B003971A4 /* ErrorView.swift */; };
3525D8A42C4AB3D600C21D99 /* CustomerCenterEnvironment.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3525D8A32C4AB3D500C21D99 /* CustomerCenterEnvironment.swift */; };
35272E1B26D0029300F22C3B /* DeviceCacheSubscriberAttributesTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37E35C4A795A0F056381A1B3 /* DeviceCacheSubscriberAttributesTests.swift */; };
35272E2226D0048D00F22C3B /* HTTPClientTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37E353CBE9CF2572A72A347F /* HTTPClientTests.swift */; };
Expand Down Expand Up @@ -1517,6 +1518,7 @@
351B517326D44F4B00BD2BD7 /* MockPaymentDiscount.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MockPaymentDiscount.swift; sourceTree = "<group>"; };
351B517926D44FF000BD2BD7 /* MockRequestFetcher.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MockRequestFetcher.swift; sourceTree = "<group>"; };
352137312CDBA2A700FE961B /* FeatureEvent.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FeatureEvent.swift; sourceTree = "<group>"; };
352304892D0A0F2B003971A4 /* ErrorView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ErrorView.swift; sourceTree = "<group>"; };
3525D8A32C4AB3D500C21D99 /* CustomerCenterEnvironment.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CustomerCenterEnvironment.swift; sourceTree = "<group>"; };
352B7D7827BD919B002A47DD /* DangerousSettings.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DangerousSettings.swift; sourceTree = "<group>"; };
3530C18822653E8F00D6DF52 /* AdSupport.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AdSupport.framework; path = System/Library/Frameworks/AdSupport.framework; sourceTree = SDKROOT; };
Expand Down Expand Up @@ -3563,6 +3565,7 @@
C3AD12B92C6EA61F00A4F86F /* CompatibilityNavigationStack.swift */,
C3AD12BB2C6EA69D00A4F86F /* SubscriptionDetailsView.swift */,
77372D982C6F8C7B008E59D3 /* AppUpdateWarningView.swift */,
352304892D0A0F2B003971A4 /* ErrorView.swift */,
);
path = Views;
sourceTree = "<group>";
Expand Down Expand Up @@ -6490,6 +6493,7 @@
88A543E72C37A4C40039C6A5 /* TierSelectorView.swift in Sources */,
2C74571F2CE7028E004ACE52 /* ScreenCondition.swift in Sources */,
88A543E12C37A4820039C6A5 /* TemplateView+MultiTier.swift in Sources */,
3523048A2D0A0F30003971A4 /* ErrorView.swift in Sources */,
887A60B72C1D037000E1A461 /* WatchTemplateView.swift in Sources */,
887A60722C1D037000E1A461 /* PaywallViewMode+Extensions.swift in Sources */,
887A60C32C1D037000E1A461 /* FooterView.swift in Sources */,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,4 +40,8 @@ protocol CustomerCenterPurchasesType: Sendable {

func track(customerCenterEvent: any CustomerCenterEventType)

func loadCustomerCenter() async throws -> CustomerCenterConfigData

func restorePurchases() async throws -> CustomerInfo

}
Original file line number Diff line number Diff line change
Expand Up @@ -54,4 +54,12 @@ final class CustomerCenterPurchases: CustomerCenterPurchasesType {
Purchases.shared.track(customerCenterEvent: customerCenterEvent)
}

func loadCustomerCenter() async throws -> CustomerCenterConfigData {
try await Purchases.shared.loadCustomerCenter()
}

func restorePurchases() async throws -> CustomerInfo {
try await Purchases.shared.restorePurchases()
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -69,10 +69,6 @@ import RevenueCat
}
}

var isLoaded: Bool {
return state != .notLoaded && configuration != nil
}

private let currentVersionFetcher: CurrentVersionFetcher
internal let customerCenterActionHandler: CustomerCenterActionHandler?

Expand Down Expand Up @@ -105,55 +101,20 @@ import RevenueCat

#endif

func loadPurchaseInformation() async {
func loadScreen() async {
do {
let customerInfo = try await purchasesProvider.customerInfo(fetchPolicy: .fetchCurrent)
let hasActiveProducts =
!customerInfo.activeSubscriptions.isEmpty || !customerInfo.nonSubscriptions.isEmpty

if !hasActiveProducts {
self.purchaseInformation = nil
self.state = .success
return
}

guard let activeTransaction = findActiveTransaction(customerInfo: customerInfo) else {
Logger.warning(Strings.could_not_find_subscription_information)
self.purchaseInformation = nil
throw CustomerCenterError.couldNotFindSubscriptionInformation
}

let entitlement = customerInfo.entitlements.all.values
.first(where: { $0.productIdentifier == activeTransaction.productIdentifier })

self.purchaseInformation = try await createPurchaseInformation(for: activeTransaction,
entitlement: entitlement)

try await self.loadPurchaseInformation()
try await self.loadCustomerCenterConfig()
self.state = .success
} catch {
self.state = .error(error)
}
}

func loadCustomerCenterConfig() async {
do {
self.configuration = try await Purchases.shared.loadCustomerCenter()
if let productId = configuration?.productId {
self.onUpdateAppClick = {
// productId is a positive integer, so it should be safe to construct a URL from it.
let url = URL(string: "https://itunes.apple.com/app/id\(productId)")!
URLUtilities.openURLIfNotAppExtension(url)
}
}
} catch {
self.state = .error(error)
}
}

func performRestore() async -> RestorePurchasesAlert.AlertType {
self.customerCenterActionHandler?(.restoreStarted)
do {
let customerInfo = try await Purchases.shared.restorePurchases()
let customerInfo = try await purchasesProvider.restorePurchases()
self.customerCenterActionHandler?(.restoreCompleted(customerInfo))
let hasPurchases = !customerInfo.activeSubscriptions.isEmpty || !customerInfo.nonSubscriptions.isEmpty
return hasPurchases ? .purchasesRecovered : .purchasesNotFound
Expand Down Expand Up @@ -182,6 +143,42 @@ import RevenueCat
@available(watchOS, unavailable)
private extension CustomerCenterViewModel {

func loadPurchaseInformation() async throws {
let customerInfo = try await purchasesProvider.customerInfo(fetchPolicy: .fetchCurrent)

let hasActiveProducts =
!customerInfo.activeSubscriptions.isEmpty || !customerInfo.nonSubscriptions.isEmpty

if !hasActiveProducts {
self.purchaseInformation = nil
self.state = .success
return
}

guard let activeTransaction = findActiveTransaction(customerInfo: customerInfo) else {
Logger.warning(Strings.could_not_find_subscription_information)
self.purchaseInformation = nil
throw CustomerCenterError.couldNotFindSubscriptionInformation
}

let entitlement = customerInfo.entitlements.all.values
.first(where: { $0.productIdentifier == activeTransaction.productIdentifier })

self.purchaseInformation = try await createPurchaseInformation(for: activeTransaction,
entitlement: entitlement)
}

func loadCustomerCenterConfig() async throws {
self.configuration = try await purchasesProvider.loadCustomerCenter()
if let productId = configuration?.productId {
self.onUpdateAppClick = {
// productId is a positive integer, so it should be safe to construct a URL from it.
let url = URL(string: "https://itunes.apple.com/app/id\(productId)")!
URLUtilities.openURLIfNotAppExtension(url)
}
}
}

func findActiveTransaction(customerInfo: CustomerInfo) -> Transaction? {
let activeSubscriptions = customerInfo.subscriptionsByProductIdentifier.values
.filter(\.isActive)
Expand Down
14 changes: 9 additions & 5 deletions RevenueCatUI/CustomerCenter/Views/CustomerCenterView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -71,15 +71,20 @@ public struct CustomerCenterView: View {
// swiftlint:disable:next missing_docs
public var body: some View {
Group {
if !self.viewModel.isLoaded {
switch self.viewModel.state {
case .error:
ErrorView()
case .notLoaded:
TintedProgressView()
} else {
case .success:
if let configuration = self.viewModel.configuration {
destinationView(configuration: configuration)
.environment(\.localization, configuration.localization)
.environment(\.appearance, configuration.appearance)
.environment(\.supportInformation, configuration.support)
.environment(\.customerCenterPresentationMode, self.mode)
} else {
TintedProgressView()
}
}
}
Expand All @@ -104,9 +109,8 @@ public struct CustomerCenterView: View {
private extension CustomerCenterView {

func loadInformationIfNeeded() async {
if !viewModel.isLoaded {
await viewModel.loadPurchaseInformation()
await viewModel.loadCustomerCenterConfig()
if viewModel.state == .notLoaded {
await viewModel.loadScreen()
}
}

Expand Down
60 changes: 60 additions & 0 deletions RevenueCatUI/CustomerCenter/Views/ErrorView.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
//
// 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
//
// ErrorView.swift
//
// Created by Cesar de la Vega on 11/12/24.

import SwiftUI

#if os(iOS)

@available(iOS 15.0, *)
@available(macOS, unavailable)
@available(tvOS, unavailable)
@available(watchOS, unavailable)
struct ErrorView: View {

@Environment(\.locale)
private var locale

var body: some View {
VStack(spacing: 20) {
let errorMessage: String = Localization.localizedBundle(self.locale)
.localizedString(forKey: "Something went wrong",
value: "Something went wrong",
table: nil)
CompatibilityContentUnavailableView(
String(errorMessage),
systemImage: "exclamationmark.triangle.fill",
description: nil
)
}
.padding(.vertical, 24)
.padding(.horizontal, 16)
.background(Color(uiColor: .secondarySystemGroupedBackground))
.cornerRadius(16)
}

}

#if DEBUG
@available(iOS 15.0, *)
@available(macOS, unavailable)
@available(tvOS, unavailable)
@available(watchOS, unavailable)
struct ErrorView_Previews: PreviewProvider {

static var previews: some View {
ErrorView()
}
}
#endif

#endif
1 change: 1 addition & 0 deletions RevenueCatUI/Resources/ar.lproj/Localizable.strings
Original file line number Diff line number Diff line change
Expand Up @@ -21,3 +21,4 @@
"pay_as_you_go_period" = "%@ خلال %@";
"pay_up_front_period" = "%@ مقابل %@";
"then_price_per_period" = "ثم %@";
"Something went wrong" = "هناك خطأ ما";
1 change: 1 addition & 0 deletions RevenueCatUI/Resources/bg.lproj/Localizable.strings
Original file line number Diff line number Diff line change
Expand Up @@ -21,3 +21,4 @@
"pay_as_you_go_period" = "%@ през %@";
"pay_up_front_period" = "%@ за %@";
"then_price_per_period" = "след това %@";
"Something went wrong" = "Нещо се обърка";
1 change: 1 addition & 0 deletions RevenueCatUI/Resources/ca.lproj/Localizable.strings
Original file line number Diff line number Diff line change
Expand Up @@ -21,3 +21,4 @@
"pay_as_you_go_period" = "%@ durant %@";
"pay_up_front_period" = "%@ per %@";
"then_price_per_period" = "després %@";
"Something went wrong" = "Alguna cosa ha anat malament";
1 change: 1 addition & 0 deletions RevenueCatUI/Resources/cs.lproj/Localizable.strings
Original file line number Diff line number Diff line change
Expand Up @@ -21,3 +21,4 @@
"pay_as_you_go_period" = "%@ během %@";
"pay_up_front_period" = "%@ za %@";
"then_price_per_period" = "poté %@";
"Something went wrong" = "Něco se pokazilo";
1 change: 1 addition & 0 deletions RevenueCatUI/Resources/da.lproj/Localizable.strings
Original file line number Diff line number Diff line change
Expand Up @@ -21,3 +21,4 @@
"pay_as_you_go_period" = "%@ i løbet af %@";
"pay_up_front_period" = "%@ for %@";
"then_price_per_period" = "derefter %@";
"Something went wrong" = "Noget gik galt";
1 change: 1 addition & 0 deletions RevenueCatUI/Resources/de.lproj/Localizable.strings
Original file line number Diff line number Diff line change
Expand Up @@ -21,3 +21,4 @@
"pay_as_you_go_period" = "%@ während %@";
"pay_up_front_period" = "%@ für %@";
"then_price_per_period" = "dann %@";
"Something went wrong" = "Etwas ist schief gelaufen";
1 change: 1 addition & 0 deletions RevenueCatUI/Resources/el.lproj/Localizable.strings
Original file line number Diff line number Diff line change
Expand Up @@ -21,3 +21,4 @@
"pay_as_you_go_period" = "%@ κατά τη διάρκεια %@";
"pay_up_front_period" = "%@ για %@";
"then_price_per_period" = "μετά %@";
"Something went wrong" = "Κάτι πήγε στραβά";
1 change: 1 addition & 0 deletions RevenueCatUI/Resources/en.lproj/Localizable.strings
Original file line number Diff line number Diff line change
Expand Up @@ -22,3 +22,4 @@
"pay_as_you_go_period" = "%@ during %@";
"pay_up_front_period" = "%@ for %@";
"then_price_per_period" = "then %@";
"Something went wrong" = "Something went wrong";
1 change: 1 addition & 0 deletions RevenueCatUI/Resources/en_AU.lproj/Localizable.strings
Original file line number Diff line number Diff line change
Expand Up @@ -22,3 +22,4 @@
"pay_as_you_go_period" = "%@ during %@";
"pay_up_front_period" = "%@ for %@";
"then_price_per_period" = "then %@";
"Something went wrong" = "Something went wrong";
1 change: 1 addition & 0 deletions RevenueCatUI/Resources/en_CA.lproj/Localizable.strings
Original file line number Diff line number Diff line change
Expand Up @@ -22,3 +22,4 @@
"pay_as_you_go_period" = "%@ during %@";
"pay_up_front_period" = "%@ for %@";
"then_price_per_period" = "then %@";
"Something went wrong" = "Something went wrong";
1 change: 1 addition & 0 deletions RevenueCatUI/Resources/en_GB.lproj/Localizable.strings
Original file line number Diff line number Diff line change
Expand Up @@ -22,3 +22,4 @@
"pay_as_you_go_period" = "%@ during %@";
"pay_up_front_period" = "%@ for %@";
"then_price_per_period" = "then %@";
"Something went wrong" = "Something went wrong";
1 change: 1 addition & 0 deletions RevenueCatUI/Resources/en_US.lproj/Localizable.strings
Original file line number Diff line number Diff line change
Expand Up @@ -22,3 +22,4 @@
"pay_as_you_go_period" = "%@ during %@";
"pay_up_front_period" = "%@ for %@";
"then_price_per_period" = "then %@";
"Something went wrong" = "Something went wrong";
1 change: 1 addition & 0 deletions RevenueCatUI/Resources/es_419.lproj/Localizable.strings
Original file line number Diff line number Diff line change
Expand Up @@ -22,3 +22,4 @@
"pay_as_you_go_period" = "%@ durante %@";
"pay_up_front_period" = "%@ por %@";
"then_price_per_period" = "luego %@";
"Something went wrong" = "Algo salió mal";
1 change: 1 addition & 0 deletions RevenueCatUI/Resources/es_ES.lproj/Localizable.strings
Original file line number Diff line number Diff line change
Expand Up @@ -22,3 +22,4 @@
"pay_as_you_go_period" = "%@ durante %@";
"pay_up_front_period" = "%@ por %@";
"then_price_per_period" = "luego %@";
"Something went wrong" = "Algo salió mal";
1 change: 1 addition & 0 deletions RevenueCatUI/Resources/fi.lproj/Localizable.strings
Original file line number Diff line number Diff line change
Expand Up @@ -21,3 +21,4 @@
"pay_as_you_go_period" = "%@ aikana %@";
"pay_up_front_period" = "%@ hinnalla %@";
"then_price_per_period" = "sitten %@";
"Something went wrong" = "Jotain meni pieleen";
1 change: 1 addition & 0 deletions RevenueCatUI/Resources/fr_CA.lproj/Localizable.strings
Original file line number Diff line number Diff line change
Expand Up @@ -21,3 +21,4 @@
"pay_as_you_go_period" = "%@ pendant %@";
"pay_up_front_period" = "%@ pour %@";
"then_price_per_period" = "puis %@";
"Something went wrong" = "Quelque chose s'est mal passé";
1 change: 1 addition & 0 deletions RevenueCatUI/Resources/he.lproj/Localizable.strings
Original file line number Diff line number Diff line change
Expand Up @@ -21,3 +21,4 @@
"pay_as_you_go_period" = "%@ במהלך %@";
"pay_up_front_period" = "%@ עבור %@";
"then_price_per_period" = "לאחר מכן %@";
"Something went wrong" = "משהו השתבש";
1 change: 1 addition & 0 deletions RevenueCatUI/Resources/hi.lproj/Localizable.strings
Original file line number Diff line number Diff line change
Expand Up @@ -21,3 +21,4 @@
"pay_as_you_go_period" = "%@ के दौरान %@";
"pay_up_front_period" = "%@ के लिए %@";
"then_price_per_period" = "फिर %@";
"Something went wrong" = "कुछ गलत हो गया";
1 change: 1 addition & 0 deletions RevenueCatUI/Resources/hr.lproj/Localizable.strings
Original file line number Diff line number Diff line change
Expand Up @@ -21,3 +21,4 @@
"pay_as_you_go_period" = "%@ tijekom %@";
"pay_up_front_period" = "%@ za %@";
"then_price_per_period" = "zatim %@";
"Something went wrong" = "Nešto je pošlo po zlu";
1 change: 1 addition & 0 deletions RevenueCatUI/Resources/hu.lproj/Localizable.strings
Original file line number Diff line number Diff line change
Expand Up @@ -21,3 +21,4 @@
"pay_as_you_go_period" = "%@ során %@";
"pay_up_front_period" = "%@ ért %@";
"then_price_per_period" = "majd %@";
"Something went wrong" = "Valami elromlott";
1 change: 1 addition & 0 deletions RevenueCatUI/Resources/id.lproj/Localizable.strings
Original file line number Diff line number Diff line change
Expand Up @@ -21,3 +21,4 @@
"pay_as_you_go_period" = "%@ selama %@";
"pay_up_front_period" = "%@ untuk %@";
"then_price_per_period" = "kemudian %@";
"Something went wrong" = "Ada yang salah";
1 change: 1 addition & 0 deletions RevenueCatUI/Resources/it.lproj/Localizable.strings
Original file line number Diff line number Diff line change
Expand Up @@ -21,3 +21,4 @@
"pay_as_you_go_period" = "%@ durante %@";
"pay_up_front_period" = "%@ per %@";
"then_price_per_period" = "poi %@";
"Something went wrong" = "Qualcosa è andato storto";
1 change: 1 addition & 0 deletions RevenueCatUI/Resources/ja.lproj/Localizable.strings
Original file line number Diff line number Diff line change
Expand Up @@ -21,3 +21,4 @@
"pay_as_you_go_period" = "%@ の期間 %@";
"pay_up_front_period" = "%@ で %@";
"then_price_per_period" = "次に %@";
"Something went wrong" = "問題が発生しました";
1 change: 1 addition & 0 deletions RevenueCatUI/Resources/kk.lproj/Localizable.strings
Original file line number Diff line number Diff line change
Expand Up @@ -21,3 +21,4 @@
"pay_as_you_go_period" = "%@ ішінде %@";
"pay_up_front_period" = "%@ үшін %@";
"then_price_per_period" = "содан кейін %@";
"Something went wrong" = "Бірдеңе дұрыс болмады";
1 change: 1 addition & 0 deletions RevenueCatUI/Resources/ko.lproj/Localizable.strings
Original file line number Diff line number Diff line change
Expand Up @@ -21,3 +21,4 @@
"pay_as_you_go_period" = "%@ 동안 %@";
"pay_up_front_period" = "%@ 에 %@";
"then_price_per_period" = "그런 다음 %@";
"Something went wrong" = "문제가 발생했습니다";
1 change: 1 addition & 0 deletions RevenueCatUI/Resources/ms.lproj/Localizable.strings
Original file line number Diff line number Diff line change
Expand Up @@ -21,3 +21,4 @@
"pay_as_you_go_period" = "%@ semasa %@";
"pay_up_front_period" = "%@ untuk %@";
"then_price_per_period" = "kemudian %@";
"Something went wrong" = "Ada sesuatu yang tidak kena";
1 change: 1 addition & 0 deletions RevenueCatUI/Resources/nl.lproj/Localizable.strings
Original file line number Diff line number Diff line change
Expand Up @@ -21,3 +21,4 @@
"pay_as_you_go_period" = "%@ tijdens %@";
"pay_up_front_period" = "%@ voor %@";
"then_price_per_period" = "dan %@";
"Something went wrong" = "Er is iets misgegaan";
Loading

0 comments on commit f3ad189

Please sign in to comment.