Skip to content

Commit 4f4c79f

Browse files
Add BackupTestFlightEntitlementManager
1 parent 163b211 commit 4f4c79f

28 files changed

+502
-166
lines changed

Signal.xcodeproj/project.pbxproj

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2681,6 +2681,7 @@
26812681
D984F7242C21FF1600E1CA49 /* DeleteForMeOutgoingSyncMessageTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = D984F7232C21FF1600E1CA49 /* DeleteForMeOutgoingSyncMessageTest.swift */; };
26822682
D985D86629B949D20087C90C /* ChangePhoneNumberPniManager+Shims.swift in Sources */ = {isa = PBXBuildFile; fileRef = D985D86529B949D20087C90C /* ChangePhoneNumberPniManager+Shims.swift */; };
26832683
D985D86829B94EC60087C90C /* ChangePhoneNumberPniManagerTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = D985D86329B91C400087C90C /* ChangePhoneNumberPniManagerTest.swift */; };
2684+
D987C8CF2E1CBE00004072BC /* BackupTestFlightEntitlementManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = D987C8CE2E1CBDFB004072BC /* BackupTestFlightEntitlementManager.swift */; };
26842685
D98ACAA72E0CA11F00FA497F /* BackupAttachmentUploadQueueStatusManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = D98ACAA62E0CA11F00FA497F /* BackupAttachmentUploadQueueStatusManager.swift */; };
26852686
D98ACAA92E0DA71B00FA497F /* BackupPlanManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = D98ACAA82E0DA71700FA497F /* BackupPlanManager.swift */; };
26862687
D98CA2AD2DF14A890060370E /* BackupOnboardingKeyIntroViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D98CA2AC2DF14A830060370E /* BackupOnboardingKeyIntroViewController.swift */; };
@@ -6643,6 +6644,7 @@
66436644
D984F7232C21FF1600E1CA49 /* DeleteForMeOutgoingSyncMessageTest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DeleteForMeOutgoingSyncMessageTest.swift; sourceTree = "<group>"; };
66446645
D985D86329B91C400087C90C /* ChangePhoneNumberPniManagerTest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChangePhoneNumberPniManagerTest.swift; sourceTree = "<group>"; };
66456646
D985D86529B949D20087C90C /* ChangePhoneNumberPniManager+Shims.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "ChangePhoneNumberPniManager+Shims.swift"; sourceTree = "<group>"; };
6647+
D987C8CE2E1CBDFB004072BC /* BackupTestFlightEntitlementManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BackupTestFlightEntitlementManager.swift; sourceTree = "<group>"; };
66466648
D98ACAA62E0CA11F00FA497F /* BackupAttachmentUploadQueueStatusManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BackupAttachmentUploadQueueStatusManager.swift; sourceTree = "<group>"; };
66476649
D98ACAA82E0DA71700FA497F /* BackupPlanManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BackupPlanManager.swift; sourceTree = "<group>"; };
66486650
D98CA2AC2DF14A830060370E /* BackupOnboardingKeyIntroViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BackupOnboardingKeyIntroViewController.swift; sourceTree = "<group>"; };
@@ -12699,6 +12701,7 @@
1269912701
D970541D2CFE4D0200AC7954 /* BackupPaymentMethod.swift */,
1270012702
D970541E2CFE4D0200AC7954 /* BackupPaymentProcessor.swift */,
1270112703
D9E43AF32CC193D70001536E /* BackupSubscriptionManager.swift */,
12704+
D987C8CE2E1CBDFB004072BC /* BackupTestFlightEntitlementManager.swift */,
1270212705
);
1270312706
path = Backups;
1270412707
sourceTree = "<group>";
@@ -17782,6 +17785,7 @@
1778217785
D9388C912DA475230048D4F9 /* BackupSettingsStore.swift in Sources */,
1778317786
C113994B2CA1B32C00D4D90C /* BackupStickerPackDownloadStore.swift in Sources */,
1778417787
D9E43AF62CC193D70001536E /* BackupSubscriptionManager.swift in Sources */,
17788+
D987C8CF2E1CBE00004072BC /* BackupTestFlightEntitlementManager.swift in Sources */,
1778517789
F9C5CE3A289453B400548EEE /* BadgeAssets.swift in Sources */,
1778617790
D979DA162B8D1FDD000EEAB8 /* BadgeCountFetcher.swift in Sources */,
1778717791
F9C5CE37289453B400548EEE /* BadgeStore.swift in Sources */,

Signal/AppLaunch/AppEnvironment.swift

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,7 @@ public class AppEnvironment: NSObject {
7272
backupIdManager: DependenciesBridge.shared.backupIdManager,
7373
backupPlanManager: DependenciesBridge.shared.backupPlanManager,
7474
backupSubscriptionManager: DependenciesBridge.shared.backupSubscriptionManager,
75+
backupTestFlightEntitlementManager: DependenciesBridge.shared.backupTestFlightEntitlementManager,
7576
db: DependenciesBridge.shared.db,
7677
tsAccountManager: DependenciesBridge.shared.tsAccountManager,
7778
)
@@ -119,6 +120,7 @@ public class AppEnvironment: NSObject {
119120
appReadiness.runNowOrWhenAppDidBecomeReadyAsync {
120121
let backupDisablingManager = DependenciesBridge.shared.backupDisablingManager
121122
let backupSubscriptionManager = DependenciesBridge.shared.backupSubscriptionManager
123+
let backupTestFlightEntitlementManager = DependenciesBridge.shared.backupTestFlightEntitlementManager
122124
let callRecordStore = DependenciesBridge.shared.callRecordStore
123125
let callRecordQuerier = DependenciesBridge.shared.callRecordQuerier
124126
let db = DependenciesBridge.shared.db
@@ -201,7 +203,15 @@ public class AppEnvironment: NSObject {
201203
do {
202204
try await backupSubscriptionManager.redeemSubscriptionIfNecessary()
203205
} catch {
204-
owsFailDebug("Failed to redeem subscription in launch job: \(error)")
206+
owsFailDebug("Failed to redeem Backup subscription in launch job: \(error)")
207+
}
208+
}
209+
210+
Task {
211+
do {
212+
try await backupTestFlightEntitlementManager.renewEntitlementIfNecessary()
213+
} catch {
214+
owsFailDebug("Failed to renew Backup entitlement for TestFlight in launch job: \(error)")
205215
}
206216
}
207217

Signal/Backups/BackupEnablingManager.swift

Lines changed: 86 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ import SignalUI
88
import StoreKit
99
import UIKit
1010

11-
class BackupEnablingManager {
11+
final class BackupEnablingManager {
1212
struct DisplayableError: Error {
1313
let localizedActionSheetMessage: String
1414

@@ -31,6 +31,7 @@ class BackupEnablingManager {
3131
private let backupIdManager: BackupIdManager
3232
private let backupPlanManager: BackupPlanManager
3333
private let backupSubscriptionManager: BackupSubscriptionManager
34+
private let backupTestFlightEntitlementManager: BackupTestFlightEntitlementManager
3435
private let db: DB
3536
private let tsAccountManager: TSAccountManager
3637

@@ -39,13 +40,15 @@ class BackupEnablingManager {
3940
backupIdManager: BackupIdManager,
4041
backupPlanManager: BackupPlanManager,
4142
backupSubscriptionManager: BackupSubscriptionManager,
43+
backupTestFlightEntitlementManager: BackupTestFlightEntitlementManager,
4244
db: DB,
4345
tsAccountManager: TSAccountManager
4446
) {
4547
self.backupDisablingManager = backupDisablingManager
4648
self.backupIdManager = backupIdManager
4749
self.backupPlanManager = backupPlanManager
4850
self.backupSubscriptionManager = backupSubscriptionManager
51+
self.backupTestFlightEntitlementManager = backupTestFlightEntitlementManager
4952
self.db = db
5053
self.tsAccountManager = tsAccountManager
5154
}
@@ -106,74 +109,105 @@ class BackupEnablingManager {
106109
throw .genericError
107110
}
108111

109-
func setBackupPlan(newBackupPlanBlock: (BackupPlan) -> BackupPlan) async throws(DisplayableError) {
110-
do {
111-
try await db.awaitableWriteWithRollbackIfThrows { tx in
112-
let newBackupPlan = newBackupPlanBlock(backupPlanManager.backupPlan(tx: tx))
113-
try backupPlanManager.setBackupPlan(newBackupPlan, tx: tx)
114-
}
115-
} catch {
116-
owsFailDebug("Failed to set BackupPlan! \(error)")
117-
throw .genericError
118-
}
119-
}
120-
121112
switch planSelection {
122113
case .free:
123114
try await setBackupPlan { _ in .free }
124-
125115
case .paid:
126-
let purchaseResult: BackupSubscription.PurchaseResult
116+
if FeatureFlags.Backups.avoidStoreKitForTesters {
117+
try await enablePaidPlanWithoutStoreKit()
118+
} else {
119+
try await enablePaidPlanWithStoreKit()
120+
}
121+
}
122+
}
123+
124+
// MARK: -
125+
126+
private func enablePaidPlanWithStoreKit() async throws(DisplayableError) {
127+
let purchaseResult: BackupSubscription.PurchaseResult
128+
do {
129+
purchaseResult = try await backupSubscriptionManager.purchaseNewSubscription()
130+
} catch StoreKitError.networkError {
131+
throw .networkError
132+
} catch {
133+
owsFailDebug("StoreKit purchase unexpectedly failed: \(error)")
134+
throw DisplayableError(OWSLocalizedString(
135+
"CHOOSE_BACKUP_PLAN_CONFIRMATION_ERROR_PURCHASE",
136+
comment: "Message shown in an action sheet when the user tries to confirm selecting the paid plan, but encountered an error from Apple while purchasing."
137+
))
138+
}
139+
140+
switch purchaseResult {
141+
case .success:
127142
do {
128-
purchaseResult = try await backupSubscriptionManager.purchaseNewSubscription()
129-
} catch StoreKitError.networkError {
130-
throw .networkError
143+
try await self.backupSubscriptionManager.redeemSubscriptionIfNecessary()
131144
} catch {
132-
owsFailDebug("StoreKit purchase unexpectedly failed: \(error)")
145+
owsFailDebug("Unexpectedly failed to redeem subscription! \(error)")
133146
throw DisplayableError(OWSLocalizedString(
134-
"CHOOSE_BACKUP_PLAN_CONFIRMATION_ERROR_PURCHASE",
135-
comment: "Message shown in an action sheet when the user tries to confirm selecting the paid plan, but encountered an error from Apple while purchasing."
147+
"CHOOSE_BACKUP_PLAN_CONFIRMATION_ERROR_PURCHASE_REDEMPTION",
148+
comment: "Message shown in an action sheet when the user tries to confirm selecting the paid plan, but encountered an error while redeeming their completed purchase."
136149
))
137150
}
138151

139-
switch purchaseResult {
140-
case .success:
141-
do {
142-
try await self.backupSubscriptionManager.redeemSubscriptionIfNecessary()
143-
} catch {
144-
owsFailDebug("Unexpectedly failed to redeem subscription! \(error)")
145-
throw DisplayableError(OWSLocalizedString(
146-
"CHOOSE_BACKUP_PLAN_CONFIRMATION_ERROR_PURCHASE_REDEMPTION",
147-
comment: "Message shown in an action sheet when the user tries to confirm selecting the paid plan, but encountered an error while redeeming their completed purchase."
148-
))
152+
try await setBackupPlan { currentBackupPlan in
153+
let currentOptimizeLocalStorage = switch currentBackupPlan {
154+
case .disabled, .disabling, .free:
155+
false
156+
case
157+
.paid(let optimizeLocalStorage),
158+
.paidExpiringSoon(let optimizeLocalStorage),
159+
.paidAsTester(let optimizeLocalStorage):
160+
optimizeLocalStorage
149161
}
150162

151-
try await setBackupPlan { currentBackupPlan in
152-
let currentOptimizeLocalStorage = switch currentBackupPlan {
153-
case .disabled, .disabling, .free:
154-
false
155-
case .paid(let optimizeLocalStorage), .paidExpiringSoon(let optimizeLocalStorage):
156-
optimizeLocalStorage
157-
}
163+
return .paid(optimizeLocalStorage: currentOptimizeLocalStorage)
164+
}
158165

159-
return .paid(optimizeLocalStorage: currentOptimizeLocalStorage)
160-
}
166+
case .pending:
167+
// The subscription won't be redeemed until if/when the purchase
168+
// is approved, but if/when that happens BackupPlan will get set
169+
// set to .paid. For the time being, we can enable Backups as
170+
// a free-tier user!
171+
try await setBackupPlan { _ in .free }
161172

162-
case .pending:
163-
// The subscription won't be redeemed until if/when the purchase
164-
// is approved, but if/when that happens BackupPlan will get set
165-
// set to .paid. For the time being, we can enable Backups as
166-
// a free-tier user!
167-
try await setBackupPlan { _ in .free }
173+
case .userCancelled:
174+
// Do nothing – don't even dismiss "choose plan", to give
175+
// the user the chance to try again. We've reserved a Backup
176+
// ID at this point, but that's fine even if they don't end
177+
// up enabling Backups at all.
178+
break
168179

169-
case .userCancelled:
170-
// Do nothing – don't even dismiss "choose plan", to give
171-
// the user the chance to try again. We've reserved a Backup
172-
// ID at this point, but that's fine even if they don't end
173-
// up enabling Backups at all.
174-
break
180+
}
181+
}
175182

183+
private func enablePaidPlanWithoutStoreKit() async throws(DisplayableError) {
184+
do {
185+
try await backupTestFlightEntitlementManager.acquireEntitlement()
186+
} catch where error.isNetworkFailureOrTimeout {
187+
throw .networkError
188+
} catch {
189+
owsFailDebug("Unexpectedly failed to renew Backup entitlement for tester! \(error)")
190+
throw .genericError
191+
}
192+
193+
try await setBackupPlan { _ in .paidAsTester(optimizeLocalStorage: false) }
194+
}
195+
196+
// MARK: -
197+
198+
private func setBackupPlan(
199+
newBackupPlanBlock: (_ currentBackupPlan: BackupPlan) -> BackupPlan,
200+
) async throws(DisplayableError) {
201+
do {
202+
try await db.awaitableWriteWithRollbackIfThrows { tx in
203+
let currentBackupPlan = backupPlanManager.backupPlan(tx: tx)
204+
let newBackupPlan = newBackupPlanBlock(currentBackupPlan)
205+
206+
try backupPlanManager.setBackupPlan(newBackupPlan, tx: tx)
176207
}
208+
} catch {
209+
owsFailDebug("Failed to set BackupPlan! \(error)")
210+
throw .genericError
177211
}
178212
}
179213
}

0 commit comments

Comments
 (0)