Skip to content

Commit b5f8ace

Browse files
authored
Merge pull request #7870 from woocommerce/issue/7833-iap-debug
In-app purchases debug screen
2 parents 3f19009 + 794b199 commit b5f8ace

File tree

12 files changed

+107
-4
lines changed

12 files changed

+107
-4
lines changed

Networking/Networking/Model/Copiable/Models+Copiable.generated.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -170,7 +170,7 @@ extension CouponReport {
170170
}
171171

172172
extension Customer {
173-
func copy(
173+
public func copy(
174174
customerID: CopiableProp<Int64> = .copy,
175175
email: CopiableProp<String> = .copy,
176176
firstName: NullableCopiableProp<String> = .copy,

Storage/Storage/Model/Copiable/Models+Copiable.generated.swift

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ extension GeneralAppSettings {
2626
isViewAddOnsSwitchEnabled: CopiableProp<Bool> = .copy,
2727
isProductSKUInputScannerSwitchEnabled: CopiableProp<Bool> = .copy,
2828
isCouponManagementSwitchEnabled: CopiableProp<Bool> = .copy,
29+
isInAppPurchasesSwitchEnabled: CopiableProp<Bool> = .copy,
2930
knownCardReaders: CopiableProp<[String]> = .copy,
3031
lastEligibilityErrorInfo: NullableCopiableProp<EligibilityErrorInfo> = .copy,
3132
lastJetpackBenefitsBannerDismissedTime: NullableCopiableProp<Date> = .copy,
@@ -36,6 +37,7 @@ extension GeneralAppSettings {
3637
let isViewAddOnsSwitchEnabled = isViewAddOnsSwitchEnabled ?? self.isViewAddOnsSwitchEnabled
3738
let isProductSKUInputScannerSwitchEnabled = isProductSKUInputScannerSwitchEnabled ?? self.isProductSKUInputScannerSwitchEnabled
3839
let isCouponManagementSwitchEnabled = isCouponManagementSwitchEnabled ?? self.isCouponManagementSwitchEnabled
40+
let isInAppPurchasesSwitchEnabled = isInAppPurchasesSwitchEnabled ?? self.isInAppPurchasesSwitchEnabled
3941
let knownCardReaders = knownCardReaders ?? self.knownCardReaders
4042
let lastEligibilityErrorInfo = lastEligibilityErrorInfo ?? self.lastEligibilityErrorInfo
4143
let lastJetpackBenefitsBannerDismissedTime = lastJetpackBenefitsBannerDismissedTime ?? self.lastJetpackBenefitsBannerDismissedTime
@@ -47,6 +49,7 @@ extension GeneralAppSettings {
4749
isViewAddOnsSwitchEnabled: isViewAddOnsSwitchEnabled,
4850
isProductSKUInputScannerSwitchEnabled: isProductSKUInputScannerSwitchEnabled,
4951
isCouponManagementSwitchEnabled: isCouponManagementSwitchEnabled,
52+
isInAppPurchasesSwitchEnabled: isInAppPurchasesSwitchEnabled,
5053
knownCardReaders: knownCardReaders,
5154
lastEligibilityErrorInfo: lastEligibilityErrorInfo,
5255
lastJetpackBenefitsBannerDismissedTime: lastJetpackBenefitsBannerDismissedTime,

Storage/Storage/Model/GeneralAppSettings.swift

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,10 @@ public struct GeneralAppSettings: Codable, Equatable, GeneratedCopiable {
3232
///
3333
public var isCouponManagementSwitchEnabled: Bool
3434

35+
/// The state for the In-app Purchases feature switch.
36+
///
37+
public var isInAppPurchasesSwitchEnabled: Bool
38+
3539
/// A list (possibly empty) of known card reader IDs - i.e. IDs of card readers that should be reconnected to automatically
3640
/// e.g. ["CHB204909005931"]
3741
///
@@ -53,6 +57,7 @@ public struct GeneralAppSettings: Codable, Equatable, GeneratedCopiable {
5357
isViewAddOnsSwitchEnabled: Bool,
5458
isProductSKUInputScannerSwitchEnabled: Bool,
5559
isCouponManagementSwitchEnabled: Bool,
60+
isInAppPurchasesSwitchEnabled: Bool,
5661
knownCardReaders: [String],
5762
lastEligibilityErrorInfo: EligibilityErrorInfo? = nil,
5863
lastJetpackBenefitsBannerDismissedTime: Date? = nil,
@@ -66,6 +71,7 @@ public struct GeneralAppSettings: Codable, Equatable, GeneratedCopiable {
6671
self.lastEligibilityErrorInfo = lastEligibilityErrorInfo
6772
self.lastJetpackBenefitsBannerDismissedTime = lastJetpackBenefitsBannerDismissedTime
6873
self.featureAnnouncementCampaignSettings = featureAnnouncementCampaignSettings
74+
self.isInAppPurchasesSwitchEnabled = isInAppPurchasesSwitchEnabled
6975
}
7076

7177
public static var `default`: Self {
@@ -74,6 +80,7 @@ public struct GeneralAppSettings: Codable, Equatable, GeneratedCopiable {
7480
isViewAddOnsSwitchEnabled: false,
7581
isProductSKUInputScannerSwitchEnabled: false,
7682
isCouponManagementSwitchEnabled: false,
83+
isInAppPurchasesSwitchEnabled: false,
7784
knownCardReaders: [],
7885
lastEligibilityErrorInfo: nil,
7986
featureAnnouncementCampaignSettings: [:])
@@ -102,6 +109,7 @@ public struct GeneralAppSettings: Codable, Equatable, GeneratedCopiable {
102109
isViewAddOnsSwitchEnabled: isViewAddOnsSwitchEnabled,
103110
isProductSKUInputScannerSwitchEnabled: isProductSKUInputScannerSwitchEnabled,
104111
isCouponManagementSwitchEnabled: isCouponManagementSwitchEnabled,
112+
isInAppPurchasesSwitchEnabled: isInAppPurchasesSwitchEnabled,
105113
knownCardReaders: knownCardReaders,
106114
lastEligibilityErrorInfo: lastEligibilityErrorInfo,
107115
featureAnnouncementCampaignSettings: featureAnnouncementCampaignSettings
@@ -121,6 +129,7 @@ public struct GeneralAppSettings: Codable, Equatable, GeneratedCopiable {
121129
isViewAddOnsSwitchEnabled: isViewAddOnsSwitchEnabled,
122130
isProductSKUInputScannerSwitchEnabled: isProductSKUInputScannerSwitchEnabled,
123131
isCouponManagementSwitchEnabled: isCouponManagementSwitchEnabled,
132+
isInAppPurchasesSwitchEnabled: isInAppPurchasesSwitchEnabled,
124133
knownCardReaders: knownCardReaders,
125134
lastEligibilityErrorInfo: lastEligibilityErrorInfo,
126135
featureAnnouncementCampaignSettings: updatedSettings
@@ -140,6 +149,7 @@ extension GeneralAppSettings {
140149
self.isViewAddOnsSwitchEnabled = try container.decodeIfPresent(Bool.self, forKey: .isViewAddOnsSwitchEnabled) ?? false
141150
self.isProductSKUInputScannerSwitchEnabled = try container.decodeIfPresent(Bool.self, forKey: .isProductSKUInputScannerSwitchEnabled) ?? false
142151
self.isCouponManagementSwitchEnabled = try container.decodeIfPresent(Bool.self, forKey: .isCouponManagementSwitchEnabled) ?? false
152+
self.isInAppPurchasesSwitchEnabled = try container.decodeIfPresent(Bool.self, forKey: .isInAppPurchasesSwitchEnabled) ?? false
143153
self.knownCardReaders = try container.decodeIfPresent([String].self, forKey: .knownCardReaders) ?? []
144154
self.lastEligibilityErrorInfo = try container.decodeIfPresent(EligibilityErrorInfo.self, forKey: .lastEligibilityErrorInfo)
145155
self.lastJetpackBenefitsBannerDismissedTime = try container.decodeIfPresent(Date.self, forKey: .lastJetpackBenefitsBannerDismissedTime)

Storage/StorageTests/Model/AppSettings/GeneralAppSettingsTests.swift

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,7 @@ final class GeneralAppSettingsTests: XCTestCase {
6666
isViewAddOnsSwitchEnabled: true,
6767
isProductSKUInputScannerSwitchEnabled: true,
6868
isCouponManagementSwitchEnabled: true,
69+
isInAppPurchasesSwitchEnabled: false,
6970
knownCardReaders: readers,
7071
lastEligibilityErrorInfo: eligibilityInfo,
7172
lastJetpackBenefitsBannerDismissedTime: jetpackBannerDismissedDate,
@@ -101,6 +102,7 @@ private extension GeneralAppSettingsTests {
101102
isViewAddOnsSwitchEnabled: Bool = false,
102103
isProductSKUInputScannerSwitchEnabled: Bool = false,
103104
isCouponManagementSwitchEnabled: Bool = false,
105+
isInAppPurchasesSwitchEnabled: Bool = false,
104106
knownCardReaders: [String] = [],
105107
lastEligibilityErrorInfo: EligibilityErrorInfo? = nil,
106108
lastJetpackBenefitsBannerDismissedTime: Date? = nil,
@@ -111,6 +113,7 @@ private extension GeneralAppSettingsTests {
111113
isViewAddOnsSwitchEnabled: isViewAddOnsSwitchEnabled,
112114
isProductSKUInputScannerSwitchEnabled: isProductSKUInputScannerSwitchEnabled,
113115
isCouponManagementSwitchEnabled: isCouponManagementSwitchEnabled,
116+
isInAppPurchasesSwitchEnabled: isInAppPurchasesSwitchEnabled,
114117
knownCardReaders: knownCardReaders,
115118
lastEligibilityErrorInfo: lastEligibilityErrorInfo,
116119
lastJetpackBenefitsBannerDismissedTime: lastJetpackBenefitsBannerDismissedTime,

WooCommerce/Classes/Model/BetaFeature.swift

Lines changed: 31 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ enum BetaFeature: String, CaseIterable {
44
case viewAddOns
55
case productSKUScanner
66
case couponManagement
7+
case inAppPurchases
78
}
89

910
extension BetaFeature {
@@ -15,6 +16,8 @@ extension BetaFeature {
1516
return Localization.productSKUScannerTitle
1617
case .couponManagement:
1718
return Localization.couponManagementTitle
19+
case .inAppPurchases:
20+
return Localization.inAppPurchasesManagementTitle
1821
}
1922
}
2023

@@ -26,6 +29,8 @@ extension BetaFeature {
2629
return Localization.productSKUScannerDescription
2730
case .couponManagement:
2831
return Localization.couponManagementDescription
32+
case .inAppPurchases:
33+
return Localization.inAppPurchasesManagementDescription
2934
}
3035
}
3136

@@ -37,6 +42,8 @@ extension BetaFeature {
3742
return \.isProductSKUInputScannerSwitchEnabled
3843
case .couponManagement:
3944
return \.isCouponManagementSwitchEnabled
45+
case .inAppPurchases:
46+
return \.isInAppPurchasesSwitchEnabled
4047
}
4148
}
4249

@@ -51,6 +58,19 @@ extension BetaFeature {
5158
}
5259
}
5360

61+
var isAvailable: Bool {
62+
switch self {
63+
case .inAppPurchases:
64+
return ServiceLocator.featureFlagService.isFeatureFlagEnabled(.inAppPurchases)
65+
default:
66+
return true
67+
}
68+
}
69+
70+
static var availableFeatures: [Self] {
71+
allCases.filter(\.isAvailable)
72+
}
73+
5474
func analyticsProperties(toggleState enabled: Bool) -> [String: WooAnalyticsEventPropertyType] {
5575
var properties = ["state": enabled ? "on" : "off"]
5676
if analyticsStat == .settingsBetaFeatureToggled {
@@ -62,7 +82,10 @@ extension BetaFeature {
6282

6383
extension GeneralAppSettingsStorage {
6484
func betaFeatureEnabled(_ feature: BetaFeature) -> Bool {
65-
value(for: feature.settingsKey)
85+
guard feature.isAvailable else {
86+
return false
87+
}
88+
return value(for: feature.settingsKey)
6689
}
6790

6891
func betaFeatureEnabledBinding(_ feature: BetaFeature) -> Binding<Bool> {
@@ -109,5 +132,12 @@ private extension BetaFeature {
109132
static let couponManagementDescription = NSLocalizedString(
110133
"Test out managing coupons as we get ready to launch",
111134
comment: "Cell description on beta features screen to enable coupon management")
135+
136+
static let inAppPurchasesManagementTitle = NSLocalizedString(
137+
"In-app purchases",
138+
comment: "Cell title on beta features screen to enable in-app purchases")
139+
static let inAppPurchasesManagementDescription = NSLocalizedString(
140+
"Test out in-app purchases as we get ready to launch",
141+
comment: "Cell description on beta features screen to enable in-app purchases")
112142
}
113143
}

WooCommerce/Classes/ViewRelated/Dashboard/Settings/Beta features/BetaFeaturesConfiguration.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ struct BetaFeaturesConfiguration: View {
1717

1818
var body: some View {
1919
List {
20-
ForEach(BetaFeature.allCases) { feature in
20+
ForEach(BetaFeature.availableFeatures) { feature in
2121
Section(footer: Text(feature.description)) {
2222
TitleAndToggleRow(title: feature.title, isOn: appSettings.betaFeatureEnabledBinding(feature))
2323
}

WooCommerce/Classes/ViewRelated/Hub Menu/HubMenu.swift

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ struct HubMenu: View {
1616
@State private var showingInbox = false
1717
@State private var showingReviews = false
1818
@State private var showingCoupons = false
19+
@State private var showingIAPDebug = false
1920

2021
/// State to disable multiple taps on menu items
2122
/// Make sure to reset the value to false after dismissing sub-flows
@@ -73,6 +74,8 @@ struct HubMenu: View {
7374
showingReviews = true
7475
case HubMenuViewModel.Coupons.id:
7576
showingCoupons = true
77+
case HubMenuViewModel.InAppPurchases.id:
78+
showingIAPDebug = true
7679
default:
7780
break
7881
}
@@ -110,6 +113,9 @@ struct HubMenu: View {
110113
NavigationLink(destination: CouponListView(siteID: viewModel.siteID), isActive: $showingCoupons) {
111114
EmptyView()
112115
}.hidden()
116+
NavigationLink(destination: InAppPurchasesDebugView(), isActive: $showingIAPDebug) {
117+
EmptyView()
118+
}.hidden()
113119
LazyNavigationLink(destination: viewModel.getReviewDetailDestination(), isActive: $viewModel.showingReviewDetail) {
114120
EmptyView()
115121
}

WooCommerce/Classes/ViewRelated/Hub Menu/HubMenuViewModel.swift

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import SwiftUI
44
import Combine
55
import Experiments
66
import Yosemite
7+
import Storage
78

89
extension NSNotification.Name {
910
/// Posted whenever the hub menu view did appear.
@@ -54,6 +55,7 @@ final class HubMenuViewModel: ObservableObject {
5455

5556
private let stores: StoresManager
5657
private let featureFlagService: FeatureFlagService
58+
private let generalAppSettings: GeneralAppSettingsStorage
5759

5860
private var productReviewFromNoteParcel: ProductReviewFromNoteParcel?
5961

@@ -64,11 +66,13 @@ final class HubMenuViewModel: ObservableObject {
6466
init(siteID: Int64,
6567
navigationController: UINavigationController? = nil,
6668
featureFlagService: FeatureFlagService = ServiceLocator.featureFlagService,
67-
stores: StoresManager = ServiceLocator.stores) {
69+
stores: StoresManager = ServiceLocator.stores,
70+
generalAppSettings: GeneralAppSettingsStorage = ServiceLocator.generalAppSettings) {
6871
self.siteID = siteID
6972
self.navigationController = navigationController
7073
self.stores = stores
7174
self.featureFlagService = featureFlagService
75+
self.generalAppSettings = generalAppSettings
7276
observeSiteForUIUpdates()
7377
}
7478

@@ -80,6 +84,9 @@ final class HubMenuViewModel: ObservableObject {
8084
///
8185
func setupMenuElements() {
8286
menuElements = [Payments(), WoocommerceAdmin(), ViewStore(), Reviews()]
87+
if generalAppSettings.betaFeatureEnabled(.inAppPurchases) {
88+
menuElements.append(InAppPurchases())
89+
}
8390

8491
let inboxUseCase = InboxEligibilityUseCase(stores: stores, featureFlagService: featureFlagService)
8592
inboxUseCase.isEligibleForInbox(siteID: siteID) { [weak self] isInboxMenuShown in
@@ -259,6 +266,17 @@ extension HubMenuViewModel {
259266
let trackingOption: String = "reviews"
260267
}
261268

269+
struct InAppPurchases: HubMenuItem {
270+
static var id = "iap"
271+
272+
let title: String = "[Debug] IAP"
273+
let icon: UIImage = UIImage(systemName: "ladybug.fill")!
274+
let iconColor: UIColor = .red
275+
let badge: HubMenuBadgeType = .number(number: 0)
276+
let accessibilityIdentifier: String = "menu-iap"
277+
let trackingOption: String = "debug-iap"
278+
}
279+
262280
private enum Localization {
263281
static let payments = NSLocalizedString("Payments",
264282
comment: "Title of the hub menu payments button")
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
import SwiftUI
2+
import StoreKit
3+
4+
struct InAppPurchasesDebugView: View {
5+
var body: some View {
6+
Group {
7+
Text("No products available")
8+
}
9+
.navigationTitle("IAP Debug")
10+
}
11+
}
12+
13+
struct InAppPurchasesDebugView_Previews: PreviewProvider {
14+
static var previews: some View {
15+
InAppPurchasesDebugView()
16+
}
17+
}

WooCommerce/WooCommerce.xcodeproj/project.pbxproj

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1776,6 +1776,7 @@
17761776
E12AF69B26BA8B2000C371C1 /* CardPresentPaymentsOnboardingUseCaseTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = E12AF69A26BA8B2000C371C1 /* CardPresentPaymentsOnboardingUseCaseTests.swift */; };
17771777
E12FB786266E0CAE0039E9C2 /* ApllicationLogDetailView.swift in Sources */ = {isa = PBXBuildFile; fileRef = E12FB785266E0CAE0039E9C2 /* ApllicationLogDetailView.swift */; };
17781778
E1308381270311E200D5A68D /* CardPresentModalUpdateFailedLowBattery.swift in Sources */ = {isa = PBXBuildFile; fileRef = E1308380270311E200D5A68D /* CardPresentModalUpdateFailedLowBattery.swift */; };
1779+
E1325EFB28FD544E00EC9B2A /* InAppPurchasesDebugView.swift in Sources */ = {isa = PBXBuildFile; fileRef = E1325EFA28FD544E00EC9B2A /* InAppPurchasesDebugView.swift */; };
17791780
E138D4F4269ED9C3006EA5C6 /* InPersonPaymentsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = E138D4F3269ED9C3006EA5C6 /* InPersonPaymentsViewController.swift */; };
17801781
E138D4FC269EEAFE006EA5C6 /* InPersonPaymentsViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = E138D4FB269EEAFE006EA5C6 /* InPersonPaymentsViewModel.swift */; };
17811782
E15F163126C5117300D3059B /* InPersonPaymentsNoConnectionView.swift in Sources */ = {isa = PBXBuildFile; fileRef = E15F163026C5117300D3059B /* InPersonPaymentsNoConnectionView.swift */; };
@@ -3706,6 +3707,7 @@
37063707
E12AF69A26BA8B2000C371C1 /* CardPresentPaymentsOnboardingUseCaseTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CardPresentPaymentsOnboardingUseCaseTests.swift; sourceTree = "<group>"; };
37073708
E12FB785266E0CAE0039E9C2 /* ApllicationLogDetailView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ApllicationLogDetailView.swift; sourceTree = "<group>"; };
37083709
E1308380270311E200D5A68D /* CardPresentModalUpdateFailedLowBattery.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CardPresentModalUpdateFailedLowBattery.swift; sourceTree = "<group>"; };
3710+
E1325EFA28FD544E00EC9B2A /* InAppPurchasesDebugView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InAppPurchasesDebugView.swift; sourceTree = "<group>"; };
37093711
E138D4F3269ED9C3006EA5C6 /* InPersonPaymentsViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InPersonPaymentsViewController.swift; sourceTree = "<group>"; };
37103712
E138D4FB269EEAFE006EA5C6 /* InPersonPaymentsViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InPersonPaymentsViewModel.swift; sourceTree = "<group>"; };
37113713
E15F163026C5117300D3059B /* InPersonPaymentsNoConnectionView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InPersonPaymentsNoConnectionView.swift; sourceTree = "<group>"; };
@@ -6757,6 +6759,7 @@
67576759
B56DB3EF2049C06D00D4AA8E /* ViewRelated */ = {
67586760
isa = PBXGroup;
67596761
children = (
6762+
E1325EF928FD543B00EC9B2A /* InAppPurchases */,
67606763
03FBDA9B263AD47200ACE257 /* Coupons */,
67616764
571B850024CF7E1600CF58A7 /* InAppFeedback */,
67626765
0235594024496414004BE2B8 /* BottomSheet */,
@@ -8490,6 +8493,14 @@
84908493
path = Settings;
84918494
sourceTree = "<group>";
84928495
};
8496+
E1325EF928FD543B00EC9B2A /* InAppPurchases */ = {
8497+
isa = PBXGroup;
8498+
children = (
8499+
E1325EFA28FD544E00EC9B2A /* InAppPurchasesDebugView.swift */,
8500+
);
8501+
path = InAppPurchases;
8502+
sourceTree = "<group>";
8503+
};
84938504
E138D4F2269ED99A006EA5C6 /* In-Person Payments */ = {
84948505
isa = PBXGroup;
84958506
children = (
@@ -9576,6 +9587,7 @@
95769587
D8C11A4E22DD235F00D4A88D /* OrderDetailsResultsControllers.swift in Sources */,
95779588
D8C251D9230D256F00F49782 /* NoticePresenter.swift in Sources */,
95789589
77E53EB8250E6A4E003D385F /* ProductDownloadListViewController.swift in Sources */,
9590+
E1325EFB28FD544E00EC9B2A /* InAppPurchasesDebugView.swift in Sources */,
95799591
74460D4022289B7600D7316A /* Coordinator.swift in Sources */,
95809592
B57C743D20F5493300EEFC87 /* AccountHeaderView.swift in Sources */,
95819593
31AD0B1126E9575F000B6391 /* CardPresentModalConnectingFailed.swift in Sources */,

0 commit comments

Comments
 (0)