Skip to content

Commit 43e4592

Browse files
committed
Hold strong ref of ApplicationPasswordsExperimentState to keep streams alive
1 parent d3dd70e commit 43e4592

File tree

5 files changed

+41
-14
lines changed

5 files changed

+41
-14
lines changed

WooCommerce/Classes/POS/TabBar/POSTabCoordinator.swift

Lines changed: 12 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ final class POSTabCoordinator {
3737
PointOfSaleItemFetchStrategyFactory(siteID: siteID,
3838
credentials: credentials,
3939
selectedSite: defaultSitePublisher,
40-
appPasswordSupportState: appPasswordSupportState)
40+
appPasswordSupportState: isAppPasswordSupported)
4141
}()
4242

4343
private lazy var posPopularItemFetchStrategyFactory: PointOfSaleFixedItemFetchStrategyFactory = {
@@ -49,7 +49,7 @@ final class POSTabCoordinator {
4949
currencySettings: currencySettings,
5050
credentials: credentials,
5151
selectedSite: defaultSitePublisher,
52-
appPasswordSupportState: appPasswordSupportState,
52+
appPasswordSupportState: isAppPasswordSupported,
5353
storage: storageManager)
5454
}()
5555

@@ -58,23 +58,25 @@ final class POSTabCoordinator {
5858
currencySettings: currencySettings,
5959
credentials: credentials,
6060
selectedSite: defaultSitePublisher,
61-
appPasswordSupportState: appPasswordSupportState,
61+
appPasswordSupportState: isAppPasswordSupported,
6262
storage: storageManager)
6363
}()
6464

6565
private lazy var barcodeScanService: PointOfSaleBarcodeScanService = {
6666
PointOfSaleBarcodeScanService(siteID: siteID,
6767
credentials: credentials,
6868
selectedSite: defaultSitePublisher,
69-
appPasswordSupportState: appPasswordSupportState,
69+
appPasswordSupportState: isAppPasswordSupported,
7070
currencySettings: currencySettings)
7171
}()
7272

7373
/// Publisher to send to `AlamofireNetwork` for request authentication mode switching.
7474
private let defaultSitePublisher: AnyPublisher<JetpackSite?, Never>
7575

76+
private let appPasswordSupportState: ApplicationPasswordsExperimentState
77+
7678
/// Publisher to send to `AlamofireNetwork` the state of app password support for JP sites
77-
private let appPasswordSupportState: AnyPublisher<Bool, Never>
79+
private let isAppPasswordSupported: AnyPublisher<Bool, Never>
7880

7981
init(siteID: Int64,
8082
tabContainerController: TabContainerController,
@@ -90,6 +92,7 @@ final class POSTabCoordinator {
9092
.map { $0?.toJetpackSite() }
9193
.eraseToAnyPublisher()
9294
self.appPasswordSupportState = ApplicationPasswordsExperimentState()
95+
self.isAppPasswordSupported = appPasswordSupportState
9396
.$isAvailableAndEnabled
9497
.eraseToAnyPublisher()
9598
self.tabContainerController = tabContainerController
@@ -119,19 +122,19 @@ private extension POSTabCoordinator {
119122
let settingsService = PointOfSaleSettingsService(siteID: siteID,
120123
credentials: credentials,
121124
selectedSite: defaultSitePublisher,
122-
appPasswordSupportState: appPasswordSupportState,
125+
appPasswordSupportState: isAppPasswordSupported,
123126
storage: storageManager)
124127
let pluginsService = PluginsService(storageManager: storageManager)
125128
let siteTimezone = storesManager.sessionManager.defaultSite?.siteTimezone ?? .current
126129

127130
if let receiptService = POSReceiptService(siteID: siteID,
128131
credentials: credentials,
129132
selectedSite: defaultSitePublisher,
130-
appPasswordSupportState: appPasswordSupportState),
133+
appPasswordSupportState: isAppPasswordSupported),
131134
let orderService = POSOrderService(siteID: siteID,
132135
credentials: credentials,
133136
selectedSite: defaultSitePublisher,
134-
appPasswordSupportState: appPasswordSupportState),
137+
appPasswordSupportState: isAppPasswordSupported),
135138
#available(iOS 17.0, *) {
136139
let receiptSender = POSReceiptSender(siteID: siteID,
137140
orderService: orderService,
@@ -156,7 +159,7 @@ private extension POSTabCoordinator {
156159
siteID: siteID,
157160
credentials: credentials,
158161
selectedSite: defaultSitePublisher,
159-
appPasswordSupportState: appPasswordSupportState,
162+
appPasswordSupportState: isAppPasswordSupported,
160163
currencyFormatter: CurrencyFormatter(currencySettings: currencySettings)
161164
)
162165
),

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

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,24 @@
1+
import Combine
12
import Foundation
23
import Yosemite
4+
import struct Storage.GeneralAppSettingsStorage
35

46
final class ApplicationPasswordsExperimentState {
57
private let stores: StoresManager
68
private let availabilityChecker: ApplicationPasswordsExperimentAvailabilityCheckerProtocol
9+
private let generalAppSettings: GeneralAppSettingsStorage
10+
11+
private var experimentalFlagSubscription: AnyCancellable?
712

813
init(
914
stores: StoresManager = ServiceLocator.stores,
10-
availabilityChecker: ApplicationPasswordsExperimentAvailabilityCheckerProtocol = ApplicationPasswordsExperimentAvailabilityChecker()
15+
availabilityChecker: ApplicationPasswordsExperimentAvailabilityCheckerProtocol = ApplicationPasswordsExperimentAvailabilityChecker(),
16+
generalAppSettings: GeneralAppSettingsStorage = ServiceLocator.generalAppSettings
1117
) {
1218
self.stores = stores
1319
self.availabilityChecker = availabilityChecker
14-
updateAvailability()
20+
self.generalAppSettings = generalAppSettings
21+
observeExperimentalFlag()
1522
}
1623

1724
@Published private(set) var isAvailableAndEnabled: Bool = true
@@ -36,6 +43,17 @@ final class ApplicationPasswordsExperimentState {
3643
isAvailableAndEnabled = isAvailable && isEnabled
3744
}
3845
}
46+
47+
private func observeExperimentalFlag() {
48+
experimentalFlagSubscription = generalAppSettings
49+
.betaFeatureEnabledPublisher(
50+
.applicationPasswords
51+
)
52+
.removeDuplicates()
53+
.sink { [weak self] _ in
54+
self?.updateAvailability()
55+
}
56+
}
3957
}
4058

4159
protocol ApplicationPasswordsExperimentAvailabilityCheckerProtocol {

WooCommerce/Classes/ViewRelated/Dashboard/Settings/POS/POSTabEligibilityChecker.swift

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@ final class POSTabEligibilityChecker: POSEntryPointEligibilityCheckerProtocol {
5454
private let featureFlagService: FeatureFlagService
5555
private let systemStatusService: POSSystemStatusServiceProtocol
5656
private let siteSettingService: POSSiteSettingServiceProtocol
57+
private let appPasswordSupportState: ApplicationPasswordsExperimentState
5758

5859
init(siteID: Int64,
5960
userInterfaceIdiom: UIUserInterfaceIdiom = UIDevice.current.userInterfaceIdiom,
@@ -69,10 +70,11 @@ final class POSTabEligibilityChecker: POSEntryPointEligibilityCheckerProtocol {
6970
self.eligibilityService = eligibilityService
7071
self.stores = stores
7172
self.featureFlagService = featureFlagService
73+
self.appPasswordSupportState = ApplicationPasswordsExperimentState()
7274

7375
let credentials = stores.sessionManager.defaultCredentials
7476
let selectedSite = stores.sessionManager.defaultSitePublisher.map { $0?.toJetpackSite() }.eraseToAnyPublisher()
75-
let appPasswordSupportState = ApplicationPasswordsExperimentState().$isAvailableAndEnabled.eraseToAnyPublisher()
77+
let appPasswordSupportState = appPasswordSupportState.$isAvailableAndEnabled.eraseToAnyPublisher()
7678
self.systemStatusService = systemStatusService ?? POSSystemStatusService(
7779
credentials: credentials,
7880
selectedSite: selectedSite,

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

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,7 @@ final class HubMenuViewModel: ObservableObject {
8686
private let blazeEligibilityChecker: BlazeEligibilityCheckerProtocol
8787
private let googleAdsEligibilityChecker: GoogleAdsEligibilityChecker
8888
private let siteCIABEligibilityChecker: CIABEligibilityCheckerProtocol
89+
private let appPasswordSupportState = ApplicationPasswordsExperimentState()
8990

9091
private(set) lazy var inboxViewModel = InboxViewModel(siteID: siteID)
9192

@@ -117,7 +118,7 @@ final class HubMenuViewModel: ObservableObject {
117118
selectedSite: stores.sessionManager.defaultSitePublisher
118119
.map { $0?.toJetpackSite() }
119120
.eraseToAnyPublisher(),
120-
appPasswordSupportState: ApplicationPasswordsExperimentState()
121+
appPasswordSupportState: appPasswordSupportState
121122
.$isAvailableAndEnabled
122123
.eraseToAnyPublisher()
123124
)

WooCommerce/Classes/Yosemite/AuthenticatedState.swift

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ class AuthenticatedState: StoresManagerState {
3737
///
3838
private(set) var posCatalogSyncCoordinator: POSCatalogSyncCoordinator?
3939

40+
private var appPasswordSupportStateHandler: ApplicationPasswordsExperimentState?
4041
private var appPasswordSupportState: PassthroughSubject<Bool, Never>
4142

4243
/// Designated Initializer
@@ -254,7 +255,9 @@ private extension AuthenticatedState {
254255
DispatchQueue.main.async { [self] in
255256
/// The state needs to be created on the main thread to avoid creating a new ServiceLocator.stores in a different thread.
256257
/// Without this, race condition can happen.
257-
ApplicationPasswordsExperimentState()
258+
let appPasswordSupportStateHandler = ApplicationPasswordsExperimentState()
259+
self.appPasswordSupportStateHandler = appPasswordSupportStateHandler // strong ref to keep the stream alive
260+
appPasswordSupportStateHandler
258261
.$isAvailableAndEnabled
259262
.receive(on: DispatchQueue.main)
260263
.sink { [weak self] enabled in

0 commit comments

Comments
 (0)