diff options
| author | Andrej Mihajlov <and@mullvad.net> | 2022-11-01 06:56:18 +0100 |
|---|---|---|
| committer | Andrej Mihajlov <and@mullvad.net> | 2022-11-02 14:05:28 +0100 |
| commit | 6127bb3da472abb5bcc37e52d935383ea0ef9569 (patch) | |
| tree | b881bc261044c74bf15c006b8a3840c76fee9f70 /ios | |
| parent | b989773b5496365a1174d359ef48433629f948e3 (diff) | |
| download | mullvadvpn-6127bb3da472abb5bcc37e52d935383ea0ef9569.tar.xz mullvadvpn-6127bb3da472abb5bcc37e52d935383ea0ef9569.zip | |
Refactor out of time controller and add interactor
Diffstat (limited to 'ios')
| -rw-r--r-- | ios/MullvadVPN.xcodeproj/project.pbxproj | 4 | ||||
| -rw-r--r-- | ios/MullvadVPN/OutOfTimeInteractor.swift | 84 | ||||
| -rw-r--r-- | ios/MullvadVPN/OutOfTimeViewController.swift | 364 | ||||
| -rw-r--r-- | ios/MullvadVPN/SceneDelegate.swift | 27 |
4 files changed, 261 insertions, 218 deletions
diff --git a/ios/MullvadVPN.xcodeproj/project.pbxproj b/ios/MullvadVPN.xcodeproj/project.pbxproj index b092636d2d..24e2e0448c 100644 --- a/ios/MullvadVPN.xcodeproj/project.pbxproj +++ b/ios/MullvadVPN.xcodeproj/project.pbxproj @@ -136,6 +136,7 @@ 585CA70F25F8C44600B47C62 /* UIMetrics.swift in Sources */ = {isa = PBXBuildFile; fileRef = 585CA70E25F8C44600B47C62 /* UIMetrics.swift */; }; 585E820327F3285E00939F0E /* SendStoreReceiptOperation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 585E820227F3285E00939F0E /* SendStoreReceiptOperation.swift */; }; 5862805422428EF100F5A6E1 /* TranslucentButtonBlurView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5862805322428EF100F5A6E1 /* TranslucentButtonBlurView.swift */; }; + 5867770E29096984006F721F /* OutOfTimeInteractor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5867770D29096984006F721F /* OutOfTimeInteractor.swift */; }; 5867771429097BCD006F721F /* PaymentState.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5867771329097BCD006F721F /* PaymentState.swift */; }; 5867771629097C5B006F721F /* ProductState.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5867771529097C5B006F721F /* ProductState.swift */; }; 5868585524054096000B8131 /* AppButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5868585424054096000B8131 /* AppButton.swift */; }; @@ -623,6 +624,7 @@ 585E820227F3285E00939F0E /* SendStoreReceiptOperation.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SendStoreReceiptOperation.swift; sourceTree = "<group>"; }; 5862805322428EF100F5A6E1 /* TranslucentButtonBlurView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TranslucentButtonBlurView.swift; sourceTree = "<group>"; }; 5866F39B2243B82D00168AE5 /* MullvadVPN.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = MullvadVPN.entitlements; sourceTree = "<group>"; }; + 5867770D29096984006F721F /* OutOfTimeInteractor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OutOfTimeInteractor.swift; sourceTree = "<group>"; }; 5867771329097BCD006F721F /* PaymentState.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PaymentState.swift; sourceTree = "<group>"; }; 5867771529097C5B006F721F /* ProductState.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProductState.swift; sourceTree = "<group>"; }; 5868585424054096000B8131 /* AppButton.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppButton.swift; sourceTree = "<group>"; }; @@ -1282,6 +1284,7 @@ 586A950B2901250A007BAF2B /* Operations */, E1187ABB289BBB850024E748 /* OutOfTimeContentView.swift */, E1187ABA289BBB850024E748 /* OutOfTimeViewController.swift */, + 5867770D29096984006F721F /* OutOfTimeInteractor.swift */, 584D26C3270C855A004EA533 /* PreferencesDataSource.swift */, 587EB6732714520600123C75 /* PreferencesDataSourceDelegate.swift */, 58ACF6482655365700ACE4B7 /* PreferencesViewController.swift */, @@ -2075,6 +2078,7 @@ 582BB1B3229574F40055B6EF /* SettingsAccountCell.swift in Sources */, 588527B2276B3F0700BAA373 /* LoadTunnelConfigurationOperation.swift in Sources */, 58F1311527E0B2AB007AC5BC /* Result+Extensions.swift in Sources */, + 5867770E29096984006F721F /* OutOfTimeInteractor.swift in Sources */, 5872631B283F6EAB00E14ADF /* Intents.intentdefinition in Sources */, 58F8AC0E25D3F8CE002BE0ED /* ProblemReportReviewViewController.swift in Sources */, 58CCA010224249A1004F3011 /* ConnectViewController.swift in Sources */, diff --git a/ios/MullvadVPN/OutOfTimeInteractor.swift b/ios/MullvadVPN/OutOfTimeInteractor.swift new file mode 100644 index 0000000000..068a299c3e --- /dev/null +++ b/ios/MullvadVPN/OutOfTimeInteractor.swift @@ -0,0 +1,84 @@ +// +// OutOfTimeInteractor.swift +// MullvadVPN +// +// Created by pronebird on 26/10/2022. +// Copyright © 2022 Mullvad VPN AB. All rights reserved. +// + +import Foundation +import MullvadREST +import MullvadTypes +import Operations +import StoreKit + +final class OutOfTimeInteractor { + private let storePaymentManager: StorePaymentManager + private let tunnelManager: TunnelManager + + var didReceivePaymentEvent: ((StorePaymentEvent) -> Void)? + var didReceiveTunnelStatus: ((TunnelStatus) -> Void)? + + private var tunnelObserver: TunnelObserver? + private var paymentObserver: StorePaymentObserver? + + init(storePaymentManager: StorePaymentManager, tunnelManager: TunnelManager) { + self.storePaymentManager = storePaymentManager + self.tunnelManager = tunnelManager + + let tunnelObserver = TunnelBlockObserver( + didUpdateTunnelStatus: { [weak self] manager, tunnelStatus in + self?.didReceiveTunnelStatus?(tunnelStatus) + } + ) + + let paymentObserver = StorePaymentBlockObserver { [weak self] manager, event in + self?.didReceivePaymentEvent?(event) + } + + tunnelManager.addObserver(tunnelObserver) + storePaymentManager.addPaymentObserver(paymentObserver) + + self.tunnelObserver = tunnelObserver + self.paymentObserver = paymentObserver + } + + var tunnelStatus: TunnelStatus { + return tunnelManager.tunnelStatus + } + + var deviceState: DeviceState { + return tunnelManager.deviceState + } + + func stopTunnel() { + tunnelManager.stopTunnel() + } + + func addPayment(_ payment: SKPayment, for accountNumber: String) { + storePaymentManager.addPayment(payment, for: accountNumber) + } + + func restorePurchases( + for accountNumber: String, + completionHandler: @escaping (OperationCompletion< + REST.CreateApplePaymentResponse, + StorePaymentManagerError + >) -> Void + ) -> Cancellable { + return storePaymentManager.restorePurchases( + for: accountNumber, + completionHandler: completionHandler + ) + } + + func requestProducts( + with productIdentifiers: Set<StoreSubscription>, + completionHandler: @escaping (OperationCompletion<SKProductsResponse, Swift.Error>) -> Void + ) -> Cancellable { + return storePaymentManager.requestProducts( + with: productIdentifiers, + completionHandler: completionHandler + ) + } +} diff --git a/ios/MullvadVPN/OutOfTimeViewController.swift b/ios/MullvadVPN/OutOfTimeViewController.swift index 0a037bf281..1de9fde687 100644 --- a/ios/MullvadVPN/OutOfTimeViewController.swift +++ b/ios/MullvadVPN/OutOfTimeViewController.swift @@ -12,40 +12,62 @@ import Operations import StoreKit import UIKit -class OutOfTimeViewController: UIViewController { - weak var delegate: SettingsButtonInteractionDelegate? +protocol OutOfTimeViewControllerDelegate: AnyObject { + func outOfTimeViewControllerDidBeginPayment(_ controller: OutOfTimeViewController) + func outOfTimeViewControllerDidEndPayment(_ controller: OutOfTimeViewController) +} - private var productState: ProductState = .none - private var paymentState: PaymentState = .none +class OutOfTimeViewController: UIViewController, RootContainment { + weak var delegate: OutOfTimeViewControllerDelegate? + private let interactor: OutOfTimeInteractor private let alertPresenter = AlertPresenter() + private var productState: ProductState = .none { + didSet { + applyViewState() + } + } + + private var paymentState: PaymentState = .none { + didSet { + applyViewState() + notifyDelegate(oldValue) + } + } + private lazy var contentView = OutOfTimeContentView() override var preferredStatusBarStyle: UIStatusBarStyle { return .lightContent } - private var tunnelState: TunnelState = .disconnected { - didSet { - setNeedsHeaderBarStyleAppearanceUpdate() - applyViewState(animated: true) - } + var preferredHeaderBarPresentation: HeaderBarPresentation { + let tunnelState = interactor.tunnelStatus.state + + return HeaderBarPresentation( + style: tunnelState.isSecured ? .secured : .unsecured, + showsDivider: false + ) } - override func viewDidLoad() { - setUpContentView() - setUpButtonTargets() - setUpInAppPurchases() - addObservers() - tunnelState = TunnelManager.shared.tunnelStatus.state + var prefersHeaderBarHidden: Bool { + return false + } + + init(interactor: OutOfTimeInteractor) { + self.interactor = interactor + + super.init(nibName: nil, bundle: nil) + } + + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") } -} -// MARK: - Private Functions + override func viewDidLoad() { + super.viewDidLoad() -private extension OutOfTimeViewController { - func setUpContentView() { view.addSubview(contentView) NSLayoutConstraint.activate([ @@ -54,15 +76,12 @@ private extension OutOfTimeViewController { contentView.trailingAnchor.constraint(equalTo: view.trailingAnchor), contentView.bottomAnchor.constraint(equalTo: view.bottomAnchor), ]) - } - func setUpButtonTargets() { contentView.disconnectButton.addTarget( self, action: #selector(handleDisconnect(_:)), for: .touchUpInside ) - contentView.purchaseButton.addTarget( self, action: #selector(doPurchase), @@ -73,187 +92,139 @@ private extension OutOfTimeViewController { action: #selector(restorePurchases), for: .touchUpInside ) - } - @objc func handleDisconnect(_ sender: Any) { - TunnelManager.shared.stopTunnel() - } - - func addObservers() { - StorePaymentManager.shared.addPaymentObserver(self) - TunnelManager.shared.addObserver(self) - } - - func setEnableUserInteraction(_ enableUserInteraction: Bool) { - [contentView.purchaseButton, contentView.restoreButton] - .forEach { button in - button?.isEnabled = enableUserInteraction - } - - view.isUserInteractionEnabled = enableUserInteraction - } - - func bodyText(for tunnelState: TunnelState) -> String { - if tunnelState.isSecured { - return NSLocalizedString( - "OUT_OF_TIME_BODY_CONNECTED", - tableName: "OutOfTime", - value: "You have no more VPN time left on this account. To add more, you will need to disconnect and access the Internet with an unsecure connection.", - comment: "" - ) - } else { - return NSLocalizedString( - "OUT_OF_TIME_BODY_DISCONNECTED", - tableName: "OutOfTime", - value: "You have no more VPN time left on this account. Either buy credit on our website or redeem a voucher.", - comment: "" - ) + interactor.didReceivePaymentEvent = { [weak self] event in + self?.didReceivePaymentEvent(event) } - } -} -// MARK: - In App Purchases + interactor.didReceiveTunnelStatus = { [weak self] tunnelStatus in + self?.setNeedsHeaderBarStyleAppearanceUpdate() + } -private extension OutOfTimeViewController { - func setUpInAppPurchases() { if StorePaymentManager.canMakePayments { requestStoreProducts() } else { - setProductState(.cannotMakePurchases, animated: false) + productState = .cannotMakePurchases } } - func requestStoreProducts() { - let productKind = StoreSubscription.thirtyDays - - setProductState(.fetching(productKind), animated: true) + // MARK: - Private - _ = StorePaymentManager.shared - .requestProducts(with: [productKind]) { [weak self] completion in - let productState: ProductState = completion.value?.products.first - .map { .received($0) } ?? .failed - - self?.setProductState(productState, animated: true) - } - } - - func setPaymentState(_ newState: PaymentState, animated: Bool) { - paymentState = newState + private func requestStoreProducts() { + let productKind = StoreSubscription.thirtyDays - applyViewState(animated: animated) - } + productState = .fetching(productKind) - func setProductState(_ newState: ProductState, animated: Bool) { - productState = newState + _ = interactor.requestProducts(with: [productKind]) { [weak self] completion in + let productState: ProductState = completion.value?.products.first + .map { .received($0) } ?? .failed - applyViewState(animated: animated) + self?.productState = productState + } } - func applyViewState(animated: Bool) { + private func applyViewState() { + let tunnelState = interactor.tunnelStatus.state let isInteractionEnabled = paymentState.allowsViewInteraction let purchaseButton = contentView.purchaseButton - let isOutOfTime = TunnelManager.shared.deviceState.accountData - .map { $0.expiry < Date() } ?? false - - let actions = { [weak self] in - guard let self = self else { return } + let isOutOfTime = interactor.deviceState.accountData.map { $0.expiry < Date() } ?? false - purchaseButton.setTitle(self.productState.purchaseButtonTitle, for: .normal) - self.contentView.purchaseButton.isLoading = self.productState.isFetching + purchaseButton.setTitle(productState.purchaseButtonTitle, for: .normal) + contentView.purchaseButton.isLoading = productState.isFetching - purchaseButton.isEnabled = self.productState.isReceived && isInteractionEnabled && !self - .tunnelState.isSecured - self.contentView.restoreButton.isEnabled = isInteractionEnabled - self.contentView.disconnectButton.isEnabled = self.tunnelState.isSecured - self.contentView.disconnectButton.alpha = self.tunnelState.isSecured ? 1 : 0 - self.contentView.bodyLabel.text = self.bodyText(for: self.tunnelState) + purchaseButton.isEnabled = productState.isReceived && isInteractionEnabled && !tunnelState + .isSecured + contentView.restoreButton.isEnabled = isInteractionEnabled + contentView.disconnectButton.isEnabled = tunnelState.isSecured + contentView.disconnectButton.alpha = tunnelState.isSecured ? 1 : 0 - if !isInteractionEnabled { - self.contentView.statusActivityView.state = .activity - } else { - self.contentView.statusActivityView.state = isOutOfTime ? .failure : .success - } - - self.delegate?.viewController( - self, - didRequestSettingsButtonEnabled: isInteractionEnabled + if tunnelState.isSecured { + contentView.bodyLabel.text = NSLocalizedString( + "OUT_OF_TIME_BODY_CONNECTED", + tableName: "OutOfTime", + value: "You have no more VPN time left on this account. To add more, you will need to disconnect and access the Internet with an unsecure connection.", + comment: "" + ) + } else { + contentView.bodyLabel.text = NSLocalizedString( + "OUT_OF_TIME_BODY_DISCONNECTED", + tableName: "OutOfTime", + value: "You have no more VPN time left on this account. Either buy credit on our website or redeem a voucher.", + comment: "" ) } - if animated { - UIView.animate(withDuration: 0.25, animations: actions) + + if !isInteractionEnabled { + contentView.statusActivityView.state = .activity } else { - actions() + contentView.statusActivityView.state = isOutOfTime ? .failure : .success } view.isUserInteractionEnabled = isInteractionEnabled - isModalInPresentation = !isInteractionEnabled - - navigationItem.setHidesBackButton(!isInteractionEnabled, animated: animated) } - @objc private func doPurchase() { - guard case let .received(product) = productState, - let accountData = TunnelManager.shared.deviceState.accountData - else { - return - } - - let payment = SKPayment(product: product) - StorePaymentManager.shared.addPayment(payment, for: accountData.number) + private func notifyDelegate(_ oldPaymentState: PaymentState) { + switch (oldPaymentState, paymentState) { + case (.none, .makingPayment), (.none, .restoringPurchases): + delegate?.outOfTimeViewControllerDidBeginPayment(self) - setPaymentState(.makingPayment(payment), animated: true) - } + case (.makingPayment, .none), (.restoringPurchases, .none): + delegate?.outOfTimeViewControllerDidEndPayment(self) - @objc func restorePurchases() { - guard let accountData = TunnelManager.shared.deviceState.accountData else { - return + default: + break } + } - setPaymentState(.restoringPurchases, animated: true) + private func didReceivePaymentEvent(_ event: StorePaymentEvent) { + guard case let .makingPayment(payment) = paymentState, + payment == event.payment else { return } - _ = StorePaymentManager.shared.restorePurchases(for: accountData.number) { completion in - switch completion { - case let .success(response): - self.showAlertIfNoTimeAdded(with: response, context: .restoration) - case let .failure(error): - self.showRestorePurchasesErrorAlert(error: error) + switch event { + case .finished: + break - case .cancelled: + case let .failure(paymentFailure): + switch paymentFailure.error { + case .storePayment(SKError.paymentCancelled): break - } - self.setPaymentState(.none, animated: true) + default: + showPaymentErrorAlert(error: paymentFailure.error) + } } - } - private func showAlertIfNoTimeAdded( - with response: REST.CreateApplePaymentResponse, - context: REST.CreateApplePaymentResponse.Context - ) { - guard case .noTimeAdded = response else { return } + paymentState = .none + } + private func showPaymentErrorAlert(error: StorePaymentManagerError) { let alertController = UIAlertController( - title: response.alertTitle(context: context), - message: response.alertMessage(context: context), + title: NSLocalizedString( + "CANNOT_COMPLETE_PURCHASE_ALERT_TITLE", + tableName: "OutOfTime", + value: "Cannot complete the purchase", + comment: "" + ), + message: error.errorChainDescription, preferredStyle: .alert ) + alertController.addAction( UIAlertAction( title: NSLocalizedString( - "TIME_ADDED_ALERT_OK_ACTION", + "CANNOT_COMPLETE_PURCHASE_ALERT_OK_ACTION", tableName: "OutOfTime", value: "OK", comment: "" - ), - style: .cancel + ), style: .cancel ) ) alertPresenter.enqueue(alertController, presentingController: self) } - func showRestorePurchasesErrorAlert(error: StorePaymentManagerError) { + private func showRestorePurchasesErrorAlert(error: StorePaymentManagerError) { let alertController = UIAlertController( title: NSLocalizedString( "RESTORE_PURCHASES_FAILURE_ALERT_TITLE", @@ -264,6 +235,7 @@ private extension OutOfTimeViewController { message: error.errorChainDescription, preferredStyle: .alert ) + alertController.addAction( UIAlertAction(title: NSLocalizedString( "RESTORE_PURCHASES_FAILURE_ALERT_OK_ACTION", @@ -272,94 +244,78 @@ private extension OutOfTimeViewController { comment: "" ), style: .cancel) ) + alertPresenter.enqueue(alertController, presentingController: self) } - func showPaymentErrorAlert(error: StorePaymentManagerError) { + private func showAlertIfNoTimeAdded( + with response: REST.CreateApplePaymentResponse, + context: REST.CreateApplePaymentResponse.Context + ) { + guard case .noTimeAdded = response else { return } + let alertController = UIAlertController( - title: NSLocalizedString( - "CANNOT_COMPLETE_PURCHASE_ALERT_TITLE", - tableName: "OutOfTime", - value: "Cannot complete the purchase", - comment: "" - ), - message: error.errorChainDescription, + title: response.alertTitle(context: context), + message: response.alertMessage(context: context), preferredStyle: .alert ) alertController.addAction( UIAlertAction( title: NSLocalizedString( - "CANNOT_COMPLETE_PURCHASE_ALERT_OK_ACTION", + "TIME_ADDED_ALERT_OK_ACTION", tableName: "OutOfTime", value: "OK", comment: "" - ), style: .cancel + ), + style: .cancel ) ) alertPresenter.enqueue(alertController, presentingController: self) } -} -// MARK: - StorePaymentObserver + // MARK: - Actions -extension OutOfTimeViewController: StorePaymentObserver { - func storePaymentManager( - _ manager: StorePaymentManager, - didReceiveEvent event: StorePaymentEvent - ) { - guard case let .makingPayment(payment) = paymentState, - payment == event.payment else { return } - - switch event { - case .finished: - break - - case let .failure(paymentFailure): - switch paymentFailure.error { - case .storePayment(SKError.paymentCancelled): - break - - default: - showPaymentErrorAlert(error: paymentFailure.error) - } + @objc private func doPurchase() { + guard case let .received(product) = productState, + let accountData = interactor.deviceState.accountData + else { + return } - setPaymentState(.none, animated: true) - } -} + let payment = SKPayment(product: product) + interactor.addPayment(payment, for: accountData.number) -// MARK: - TunnelObserver + paymentState = .makingPayment(payment) + } -extension OutOfTimeViewController: TunnelObserver { - func tunnelManagerDidLoadConfiguration(_ manager: TunnelManager) {} + @objc func restorePurchases() { + guard let accountData = interactor.deviceState.accountData else { + return + } - func tunnelManager(_ manager: TunnelManager, didUpdateTunnelStatus tunnelStatus: TunnelStatus) { - tunnelState = tunnelStatus.state - } + paymentState = .restoringPurchases - func tunnelManager(_ manager: TunnelManager, didUpdateDeviceState deviceState: DeviceState) {} + _ = interactor.restorePurchases(for: accountData.number) { [weak self] completion in + guard let self = self else { return } - func tunnelManager( - _ manager: TunnelManager, - didUpdateTunnelSettings tunnelSettings: TunnelSettingsV2 - ) {} + switch completion { + case let .success(response): + self.showAlertIfNoTimeAdded(with: response, context: .restoration) - func tunnelManager(_ manager: TunnelManager, didFailWithError error: Error) {} -} + case let .failure(error): + self.showRestorePurchasesErrorAlert(error: error) -// MARK: - Header Bar + case .cancelled: + break + } -extension OutOfTimeViewController: RootContainment { - var preferredHeaderBarPresentation: HeaderBarPresentation { - return HeaderBarPresentation( - style: tunnelState.isSecured ? .secured : .unsecured, - showsDivider: false - ) + self.paymentState = .none + } } - var prefersHeaderBarHidden: Bool { - false + @objc private func handleDisconnect(_ sender: Any) { + interactor.stopTunnel() } } diff --git a/ios/MullvadVPN/SceneDelegate.swift b/ios/MullvadVPN/SceneDelegate.swift index afb431faea..00ac40f33f 100644 --- a/ios/MullvadVPN/SceneDelegate.swift +++ b/ios/MullvadVPN/SceneDelegate.swift @@ -141,21 +141,15 @@ extension SceneDelegate: UIWindowSceneDelegate { func sceneDidEnterBackground(_ scene: UIScene) {} } -// MARK: - SettingsButtonInteractionDelegate +// MARK: - OutOfTimeViewControllerDelegate -protocol SettingsButtonInteractionDelegate: AnyObject { - func viewController( - _ controller: UIViewController, - didRequestSettingsButtonEnabled isEnabled: Bool - ) -} +extension SceneDelegate: OutOfTimeViewControllerDelegate { + func outOfTimeViewControllerDidBeginPayment(_ controller: OutOfTimeViewController) { + setEnableSettingsButton(isEnabled: false, from: controller) + } -extension SceneDelegate: SettingsButtonInteractionDelegate { - func viewController( - _ controller: UIViewController, - didRequestSettingsButtonEnabled isEnabled: Bool - ) { - setEnableSettingsButton(isEnabled: isEnabled, from: controller) + func outOfTimeViewControllerDidEndPayment(_ controller: OutOfTimeViewController) { + setEnableSettingsButton(isEnabled: true, from: controller) } } @@ -369,7 +363,12 @@ extension SceneDelegate { } private func makeOutOfTimeViewController() -> OutOfTimeViewController { - let viewController = OutOfTimeViewController() + let viewController = OutOfTimeViewController( + interactor: OutOfTimeInteractor( + storePaymentManager: .shared, + tunnelManager: .shared + ) + ) viewController.delegate = self return viewController } |
