diff options
| author | Bug Magnet <marco.nikic@mullvad.net> | 2025-01-20 14:34:42 +0100 |
|---|---|---|
| committer | Bug Magnet <marco.nikic@mullvad.net> | 2025-01-20 14:34:42 +0100 |
| commit | abefbd407a6191a044fea4cc58e431118587fcd9 (patch) | |
| tree | 4eb6c0aeada3c03829d47580d1c902275914d983 | |
| parent | 7cc4ba8b2e3359150835e765a097070f3792606d (diff) | |
| parent | adc71305ee8379ff02fb67a41f76548437e540a2 (diff) | |
| download | mullvadvpn-abefbd407a6191a044fea4cc58e431118587fcd9.tar.xz mullvadvpn-abefbd407a6191a044fea4cc58e431118587fcd9.zip | |
Merge branch 'fix-crash-when-pressing-create-new-account-button-ios-1019'
9 files changed, 25 insertions, 41 deletions
diff --git a/ios/MullvadVPN/Coordinators/ApplicationCoordinator.swift b/ios/MullvadVPN/Coordinators/ApplicationCoordinator.swift index 2175835045..6c0c61b5a2 100644 --- a/ios/MullvadVPN/Coordinators/ApplicationCoordinator.swift +++ b/ios/MullvadVPN/Coordinators/ApplicationCoordinator.swift @@ -450,14 +450,10 @@ final class ApplicationCoordinator: Coordinator, Presenting, @preconcurrency Roo coordinator.preferredAccountNumberPublisher = preferredAccountNumberSubject.eraseToAnyPublisher() coordinator.didFinish = { [weak self] _ in - MainActor.assumeIsolated { - self?.continueFlow(animated: true) - } + self?.continueFlow(animated: true) } coordinator.didCreateAccount = { [weak self] in - MainActor.assumeIsolated { - self?.appPreferences.isShownOnboarding = false - } + self?.appPreferences.isShownOnboarding = false } addChild(coordinator) @@ -538,9 +534,7 @@ final class ApplicationCoordinator: Coordinator, Presenting, @preconcurrency Roo ) coordinator.didFinish = { [weak self] _, reason in - MainActor.assumeIsolated { - self?.didDismissAccount(reason) - } + self?.didDismissAccount(reason) } coordinator.start(animated: animated) diff --git a/ios/MullvadVPN/Coordinators/LoginCoordinator.swift b/ios/MullvadVPN/Coordinators/LoginCoordinator.swift index 4ac34bcbbe..c6934df365 100644 --- a/ios/MullvadVPN/Coordinators/LoginCoordinator.swift +++ b/ios/MullvadVPN/Coordinators/LoginCoordinator.swift @@ -21,8 +21,8 @@ final class LoginCoordinator: Coordinator, Presenting, @preconcurrency DeviceMan nonisolated(unsafe) private var lastLoginAction: LoginAction? private var subscriptions = Set<Combine.AnyCancellable>() - var didFinish: (@Sendable (LoginCoordinator) -> Void)? - var didCreateAccount: (@Sendable () -> Void)? + var didFinish: (@MainActor @Sendable (LoginCoordinator) -> Void)? + var didCreateAccount: (@MainActor @Sendable () -> Void)? var preferredAccountNumberPublisher: AnyPublisher<String, Never>? var presentationContext: UIViewController { diff --git a/ios/MullvadVPN/View controllers/Account/AccountViewController.swift b/ios/MullvadVPN/View controllers/Account/AccountViewController.swift index 0c7ca21532..19060381ab 100644 --- a/ios/MullvadVPN/View controllers/Account/AccountViewController.swift +++ b/ios/MullvadVPN/View controllers/Account/AccountViewController.swift @@ -161,6 +161,8 @@ class AccountViewController: UIViewController, @unchecked Sendable { let productState: ProductState = completion.value?.products.first .map { .received($0) } ?? .failed + /// `@MainActor` isolation is safe here because + /// `ProductsRequestOperation` sets its `completionQueue` to `.main` MainActor.assumeIsolated { self?.setProductState(productState, animated: true) } diff --git a/ios/MullvadVPN/View controllers/Account/PaymentAlertPresenter.swift b/ios/MullvadVPN/View controllers/Account/PaymentAlertPresenter.swift index 4e62d1a808..07369ac526 100644 --- a/ios/MullvadVPN/View controllers/Account/PaymentAlertPresenter.swift +++ b/ios/MullvadVPN/View controllers/Account/PaymentAlertPresenter.swift @@ -16,7 +16,7 @@ struct PaymentAlertPresenter { func showAlertForError( _ error: StorePaymentManagerError, context: REST.CreateApplePaymentResponse.Context, - completion: (@Sendable () -> Void)? = nil + completion: (@MainActor @Sendable () -> Void)? = nil ) { let presentation = AlertPresentation( id: "payment-error-alert", @@ -64,7 +64,7 @@ struct PaymentAlertPresenter { func showAlertForResponse( _ response: REST.CreateApplePaymentResponse, context: REST.CreateApplePaymentResponse.Context, - completion: (@Sendable () -> Void)? = nil + completion: (@MainActor @Sendable () -> Void)? = nil ) { guard case .noTimeAdded = response else { completion?() diff --git a/ios/MullvadVPN/View controllers/Login/LoginInteractor.swift b/ios/MullvadVPN/View controllers/Login/LoginInteractor.swift index cd7564f356..302f1fa30c 100644 --- a/ios/MullvadVPN/View controllers/Login/LoginInteractor.swift +++ b/ios/MullvadVPN/View controllers/Login/LoginInteractor.swift @@ -14,7 +14,7 @@ final class LoginInteractor: @unchecked Sendable { private let tunnelManager: TunnelManager private let logger = Logger(label: "LoginInteractor") private var tunnelObserver: TunnelObserver? - var didCreateAccount: (@Sendable () -> Void)? + var didCreateAccount: (@MainActor @Sendable () -> Void)? var suggestPreferredAccountNumber: (@Sendable (String) -> Void)? init(tunnelManager: TunnelManager) { @@ -27,7 +27,7 @@ final class LoginInteractor: @unchecked Sendable { func createAccount() async throws -> String { let accountNumber = try await tunnelManager.setNewAccount().number - didCreateAccount?() + await didCreateAccount?() return accountNumber } diff --git a/ios/MullvadVPN/View controllers/OutOfTime/OutOfTimeViewController.swift b/ios/MullvadVPN/View controllers/OutOfTime/OutOfTimeViewController.swift index a735fb4eca..5f26cd4643 100644 --- a/ios/MullvadVPN/View controllers/OutOfTime/OutOfTimeViewController.swift +++ b/ios/MullvadVPN/View controllers/OutOfTime/OutOfTimeViewController.swift @@ -221,9 +221,7 @@ class OutOfTimeViewController: UIViewController, RootContainment { default: errorPresenter.showAlertForError(paymentFailure.error, context: .purchase) { - MainActor.assumeIsolated { - self.paymentState = .none - } + self.paymentState = .none } } } @@ -253,30 +251,23 @@ class OutOfTimeViewController: UIViewController, RootContainment { paymentState = .restoringPurchases + /// Safe to assume `@MainActor` isolation because `SendStoreReceiptOperation` sets both its + /// `dispatchQueue` and `completionQueue` to `.main` _ = interactor.restorePurchases(for: accountData.number) { [weak self] result in guard let self else { return } - - switch result { - case let .success(response): - Task { @MainActor in + MainActor.assumeIsolated { + switch result { + case let .success(response): errorPresenter.showAlertForResponse(response, context: .restoration) { - MainActor.assumeIsolated { - self.paymentState = .none - } + self.paymentState = .none } - } - case let .failure(error as StorePaymentManagerError): - Task { @MainActor in + case let .failure(error as StorePaymentManagerError): errorPresenter.showAlertForError(error, context: .restoration) { - MainActor.assumeIsolated { - self.paymentState = .none - } + self.paymentState = .none } - } - default: - Task { @MainActor in + default: paymentState = .none } } diff --git a/ios/MullvadVPN/View controllers/RedeemVoucher/RedeemVoucherViewController.swift b/ios/MullvadVPN/View controllers/RedeemVoucher/RedeemVoucherViewController.swift index 870426c004..c62dc917ef 100644 --- a/ios/MullvadVPN/View controllers/RedeemVoucher/RedeemVoucherViewController.swift +++ b/ios/MullvadVPN/View controllers/RedeemVoucher/RedeemVoucherViewController.swift @@ -116,6 +116,8 @@ class RedeemVoucherViewController: UIViewController, UINavigationControllerDeleg contentView.isEditing = false interactor.redeemVoucher(code: code, completion: { [weak self] result in guard let self else { return } + /// Safe to assume `@MainActor` isolation because + /// `TunnelManager.redeemVoucher` sets the `RedeemVoucherOperation`'s `completionQueue` to `.main` MainActor.assumeIsolated { switch result { case let .success(value): diff --git a/ios/Operations/BackgroundObserver.swift b/ios/Operations/BackgroundObserver.swift index 5f5add27bc..3b5d6f2516 100644 --- a/ios/Operations/BackgroundObserver.swift +++ b/ios/Operations/BackgroundObserver.swift @@ -26,13 +26,9 @@ public final class BackgroundObserver: OperationObserver { } public func didAttach(to operation: Operation) { - #if swift(>=6) let expirationHandler = cancelUponExpiration ? { @MainActor in operation.cancel() } as? @MainActor @Sendable () -> Void : nil - #else - let expirationHandler = cancelUponExpiration ? { operation.cancel() } : nil - #endif taskIdentifier = backgroundTaskProvider.beginBackgroundTask( withName: name, diff --git a/ios/Routing/Router/ApplicationRouter.swift b/ios/Routing/Router/ApplicationRouter.swift index c55daf2400..0baa0662dd 100644 --- a/ios/Routing/Router/ApplicationRouter.swift +++ b/ios/Routing/Router/ApplicationRouter.swift @@ -158,9 +158,8 @@ public final class ApplicationRouter<RouteType: AppRouteProtocol>: Sendable { let context = RoutePresentationContext(route: route, isAnimated: animated, metadata: metadata) delegate.applicationRouter(self, presentWithContext: context, animated: animated) { coordinator in - /* - Synchronize router when modal controllers are removed by swipe. - */ + /// Synchronize router when modal controllers are removed by swipe. + /// The delegate (`ApplicationCoordinator`) is `@MainActor` by virtue of being a `Coordinator` MainActor.assumeIsolated { if let presentable = coordinator as? Presentable { presentable.onInteractiveDismissal { [weak self] coordinator in |
