Skip to content

Commit

Permalink
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[Customer Center] Add API call to get Customer Center config (#3933)
Browse files Browse the repository at this point in the history
Adds `Purchases.shared.loadCustomerCenter()` that calls a new backend
endpoint that returns the customer center configuration

This API call doesn't exist yet and it will change. This PR is the
ground work so that we don't have to wait for the backend to add this
API and we can already pretend the API is there.

---------

Co-authored-by: RevenueCat Git Bot <[email protected]>
vegaro and RCGitBot committed Jul 9, 2024
1 parent 02e9773 commit 49d2a2d
Showing 58 changed files with 2,296 additions and 198 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -57,3 +57,4 @@ Tests/InstallationTests/XcodeDirectInstallation/XcodeDirectInstallation.xcodepro

# fastlane
fastlane/.env
.vscode/settings.json
146 changes: 139 additions & 7 deletions RevenueCat.xcodeproj/project.pbxproj

Large diffs are not rendered by default.

63 changes: 0 additions & 63 deletions RevenueCatUI/CustomerCenter/Data/CustomerCenterConfigData.swift

This file was deleted.

104 changes: 71 additions & 33 deletions RevenueCatUI/CustomerCenter/Data/CustomerCenterConfigTestData.swift
Original file line number Diff line number Diff line change
@@ -20,50 +20,88 @@ enum CustomerCenterConfigTestData {

@available(iOS 14.0, *)
static let customerCenterData = CustomerCenterConfigData(
id: "customer_center_id",
paths: [
.init(
id: "1",
title: "Didn't receive purchase",
type: .missingPurchase,
detail: nil
),
.init(
id: "2",
title: "Request a refund",
type: .refundRequest,
detail: nil
),
.init(
id: "3",
title: "Change plans",
type: .changePlans,
detail: nil
),
.init(
id: "4",
title: "Cancel subscription",
type: .cancel,
detail: .feedbackSurvey(.init(
title: "Why are you cancelling?",
options: [
screens: [.management:
.init(
type: .management,
title: "Manage Subscription",
subtitle: "Manage your subscription details here",
paths: [
.init(
id: "1",
title: "Too expensive"
title: "Didn't receive purchase",
type: .missingPurchase,
detail: nil
),
.init(
id: "2",
title: "Don't use the app"
title: "Request a refund",
type: .refundRequest,
detail: nil
),
.init(
id: "3",
title: "Bought by mistake"
title: "Change plans",
type: .changePlans,
detail: nil
),
.init(
id: "4",
title: "Cancel subscription",
type: .cancel,
detail: .feedbackSurvey(.init(
title: "Why are you cancelling?",
options: [
.init(
id: "1",
title: "Too expensive"
),
.init(
id: "2",
title: "Don't use the app"
),
.init(
id: "3",
title: "Bought by mistake"
)
]
))
)
]
))
)
),
.noActive: .init(
type: .noActive,
title: "No Active Subscription",
subtitle: "You currently have no active subscriptions",
paths: [
.init(
id: "9q9719171o",
title: "Check purchases",
type: .missingPurchase,
detail: nil
)
]
)
],
title: "How can we help?"
appearance: .init(
mode: .custom,
light: .init(
accentColor: "#ffffff",
backgroundColor: "#000000",
textColor: "#000000"
),
dark: .init(
accentColor: "#000000",
backgroundColor: "#ffffff",
textColor: "#ffffff"
)
),
localization: .init(
locale: "en_US",
localizedStrings: [
"cancel": "Cancel",
"back": "Back"
]
)
)

static let subscriptionInformation: SubscriptionInformation = .init(
Original file line number Diff line number Diff line change
@@ -38,9 +38,11 @@ import RevenueCat
}
}
}
@Published
var configuration: CustomerCenterConfigData?

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

private var customerInfoFetcher: CustomerInfoFetcher
@@ -97,4 +99,12 @@ import RevenueCat
}
}

func loadCustomerCenterConfig() async {
do {
self.configuration = try await Purchases.shared.loadCustomerCenter()
} catch {
self.state = .error(error)
}
}

}
Original file line number Diff line number Diff line change
@@ -25,12 +25,8 @@ import RevenueCat
@MainActor
class ManageSubscriptionsViewModel: ObservableObject {

@Published
private(set) var subscriptionInformation: SubscriptionInformation?
@Published
private(set) var refundRequestStatusMessage: String?
@Published
private(set) var configuration: CustomerCenterConfigData?
let screen: CustomerCenterConfigData.Screen

@Published
var showRestoreAlert: Bool = false
@Published
@@ -41,29 +37,34 @@ class ManageSubscriptionsViewModel: ObservableObject {
}
}
}

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

@Published
private(set) var subscriptionInformation: SubscriptionInformation?
@Published
private(set) var refundRequestStatusMessage: String?

private var purchasesProvider: ManageSubscriptionsPurchaseType

private var error: Error?

convenience init() {
self.init(purchasesProvider: ManageSubscriptionPurchases())
convenience init(screen: CustomerCenterConfigData.Screen) {
self.init(screen: screen,
purchasesProvider: ManageSubscriptionPurchases())
}

// @PublicForExternalTesting
init(purchasesProvider: ManageSubscriptionsPurchaseType) {
init(screen: CustomerCenterConfigData.Screen,
purchasesProvider: ManageSubscriptionsPurchaseType) {
self.state = .notLoaded
self.screen = screen
self.purchasesProvider = purchasesProvider
}

// @PublicForExternalTesting
init(configuration: CustomerCenterConfigData,
init(screen: CustomerCenterConfigData.Screen,
subscriptionInformation: SubscriptionInformation) {
self.configuration = configuration
self.screen = screen
self.subscriptionInformation = subscriptionInformation
self.purchasesProvider = ManageSubscriptionPurchases()
state = .success
@@ -72,7 +73,6 @@ class ManageSubscriptionsViewModel: ObservableObject {
func loadScreen() async {
do {
try await loadSubscriptionInformation()
loadCustomerCenterConfig()
self.state = .success
} catch {
self.state = .error(error)
@@ -104,10 +104,6 @@ class ManageSubscriptionsViewModel: ObservableObject {
)
}

private func loadCustomerCenterConfig() {
self.configuration = CustomerCenterConfigTestData.customerCenterData
}

#if os(iOS) || targetEnvironment(macCatalyst)
func handleAction(for path: CustomerCenterConfigData.HelpPath) async {
switch path.type {
18 changes: 11 additions & 7 deletions RevenueCatUI/CustomerCenter/Views/CustomerCenterView.swift
Original file line number Diff line number Diff line change
@@ -41,11 +41,13 @@ public struct CustomerCenterView: View {
if !viewModel.isLoaded {
ProgressView()
} else {
destinationView()
if let configuration = viewModel.configuration {
destinationView(configuration: configuration)
}
}
}
.task {
await checkAndLoadSubscriptions()
await loadInformationIfNeeded()
}
}

@@ -58,22 +60,24 @@ public struct CustomerCenterView: View {
@available(visionOS, unavailable)
private extension CustomerCenterView {

func checkAndLoadSubscriptions() async {
func loadInformationIfNeeded() async {
if !viewModel.isLoaded {
await viewModel.loadHasSubscriptions()
await viewModel.loadCustomerCenterConfig()
}
}

@ViewBuilder
func destinationView() -> some View {
func destinationView(configuration: CustomerCenterConfigData) -> some View {
if viewModel.hasSubscriptions {
if viewModel.subscriptionsAreFromApple {
ManageSubscriptionsView()
if viewModel.subscriptionsAreFromApple,
let screen = configuration.screens[.management] {
ManageSubscriptionsView(screen: screen)
} else {
WrongPlatformView()
}
} else {
NoSubscriptionsView()
NoSubscriptionsView(configuration: configuration)
}
}

Loading

0 comments on commit 49d2a2d

Please sign in to comment.