summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorBug Magnet <marco.nikic@mullvad.net>2025-01-20 11:31:37 +0100
committerBug Magnet <marco.nikic@mullvad.net>2025-01-20 14:32:07 +0100
commitb650be79d4569343e4ac348f1cd276feca1188cb (patch)
treed8a598d217975311b9e6882b1cddd4f86d7766a5
parent7cc4ba8b2e3359150835e765a097070f3792606d (diff)
downloadmullvadvpn-b650be79d4569343e4ac348f1cd276feca1188cb.tar.xz
mullvadvpn-b650be79d4569343e4ac348f1cd276feca1188cb.zip
Revisit main actor isolation in the UI Process
-rw-r--r--ios/MullvadVPN/Coordinators/ApplicationCoordinator.swift12
-rw-r--r--ios/MullvadVPN/Coordinators/LoginCoordinator.swift4
-rw-r--r--ios/MullvadVPN/View controllers/Account/AccountViewController.swift2
-rw-r--r--ios/MullvadVPN/View controllers/Account/PaymentAlertPresenter.swift4
-rw-r--r--ios/MullvadVPN/View controllers/Login/LoginInteractor.swift4
-rw-r--r--ios/MullvadVPN/View controllers/OutOfTime/OutOfTimeViewController.swift29
-rw-r--r--ios/MullvadVPN/View controllers/RedeemVoucher/RedeemVoucherViewController.swift2
-rw-r--r--ios/Routing/Router/ApplicationRouter.swift5
8 files changed, 25 insertions, 37 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/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