diff options
12 files changed, 157 insertions, 254 deletions
diff --git a/ios/MullvadVPN/AppStorePaymentManager/AppStorePaymentManager.swift b/ios/MullvadVPN/AppStorePaymentManager/AppStorePaymentManager.swift index 9d4b56cf99..fab80ea9de 100644 --- a/ios/MullvadVPN/AppStorePaymentManager/AppStorePaymentManager.swift +++ b/ios/MullvadVPN/AppStorePaymentManager/AppStorePaymentManager.swift @@ -133,6 +133,17 @@ class AppStorePaymentManager: NSObject, SKPaymentTransactionObserver { didFailWithError: .validateAccount(error) ) } + + case .cancelled: + self.observerList.forEach { observer in + observer.appStorePaymentManager( + self, + transaction: nil, + payment: payment, + accountToken: accountToken, + didFailWithError: .validateAccount(.network(URLError(.cancelled))) + ) + } } UIApplication.shared.endBackgroundTask(backgroundTaskIdentifier) @@ -285,16 +296,13 @@ class AppStorePaymentManager: NSObject, SKPaymentTransactionObserver { } -private class SendAppStoreReceiptOperation: AsyncOperation { - typealias CompletionHandler = (OperationCompletion<REST.CreateApplePaymentResponse, AppStorePaymentManager.Error>) -> Void - +private class SendAppStoreReceiptOperation: ResultOperation<REST.CreateApplePaymentResponse, AppStorePaymentManager.Error> { private let restClient: REST.Client private let accountToken: String private let forceRefresh: Bool private let receiptProperties: [String: Any]? - private var completionHandler: CompletionHandler? - private var fetchReceiptCancellable: Cancellable? - private var submitReceiptCancellable: Cancellable? + private var fetchReceiptTask: Cancellable? + private var submitReceiptTask: Cancellable? private let logger = Logger(label: "AppStorePaymentManager.SendAppStoreReceiptOperation") @@ -303,18 +311,19 @@ private class SendAppStoreReceiptOperation: AsyncOperation { self.accountToken = accountToken self.forceRefresh = forceRefresh self.receiptProperties = receiptProperties - self.completionHandler = completionHandler + + super.init(completionQueue: .main, completionHandler: completionHandler) } override func cancel() { super.cancel() DispatchQueue.main.async { - self.fetchReceiptCancellable?.cancel() - self.fetchReceiptCancellable = nil + self.fetchReceiptTask?.cancel() + self.fetchReceiptTask = nil - self.submitReceiptCancellable?.cancel() - self.submitReceiptCancellable = nil + self.submitReceiptTask?.cancel() + self.submitReceiptTask = nil } } @@ -325,7 +334,7 @@ private class SendAppStoreReceiptOperation: AsyncOperation { return } - self.fetchReceiptCancellable = AppStoreReceipt.fetch(forceRefresh: self.forceRefresh, receiptProperties: self.receiptProperties) { completion in + self.fetchReceiptTask = AppStoreReceipt.fetch(forceRefresh: self.forceRefresh, receiptProperties: self.receiptProperties) { completion in switch completion { case .success(let receiptData): self.sendReceipt(receiptData) @@ -342,7 +351,7 @@ private class SendAppStoreReceiptOperation: AsyncOperation { } private func sendReceipt(_ receiptData: Data) { - submitReceiptCancellable = restClient.createApplePayment( + submitReceiptTask = restClient.createApplePayment( token: self.accountToken, receiptString: receiptData, retryStrategy: .noRetry) { result in @@ -354,15 +363,11 @@ private class SendAppStoreReceiptOperation: AsyncOperation { case .failure(let error): self.logger.error(chainedError: error, message: "Failed to send the AppStore receipt.") self.finish(completion: .failure(.sendReceipt(error))) + + case .cancelled: + self.logger.debug("Receipt submission cancelled.") + self.finish(completion: .cancelled) } } } - - private func finish(completion: OperationCompletion<REST.CreateApplePaymentResponse, AppStorePaymentManager.Error>) { - let block = completionHandler - completionHandler = nil - - block?(completion) - finish() - } } diff --git a/ios/MullvadVPN/AppStoreReceipt.swift b/ios/MullvadVPN/AppStoreReceipt.swift index ff61ffd281..849ef7ed0c 100644 --- a/ios/MullvadVPN/AppStoreReceipt.swift +++ b/ios/MullvadVPN/AppStoreReceipt.swift @@ -67,19 +67,18 @@ enum AppStoreReceipt { } } -fileprivate class FetchAppStoreReceiptOperation: AsyncOperation, SKRequestDelegate { +fileprivate class FetchAppStoreReceiptOperation: ResultOperation<Data, AppStoreReceipt.Error>, SKRequestDelegate { + private let dispatchQueue: DispatchQueue private var request: SKReceiptRefreshRequest? private let receiptProperties: [String: Any]? private let forceRefresh: Bool - private let dispatchQueue: DispatchQueue - private var completionHandler: ((OperationCompletion<Data, AppStoreReceipt.Error>) -> Void)? - - init(dispatchQueue: DispatchQueue, forceRefresh: Bool, receiptProperties: [String: Any]?, completionHandler: @escaping (OperationCompletion<Data, AppStoreReceipt.Error>) -> Void) { + init(dispatchQueue: DispatchQueue, forceRefresh: Bool, receiptProperties: [String: Any]?, completionHandler: @escaping (Completion) -> Void) { self.dispatchQueue = dispatchQueue self.forceRefresh = forceRefresh self.receiptProperties = receiptProperties - self.completionHandler = completionHandler + + super.init(completionQueue: .main, completionHandler: completionHandler) } override func main() { @@ -172,15 +171,4 @@ fileprivate class FetchAppStoreReceiptOperation: AsyncOperation, SKRequestDelega } } - private func finish(completion: OperationCompletion<Data, AppStoreReceipt.Error>) { - let block = completionHandler - completionHandler = nil - - DispatchQueue.main.async { - block?(completion) - } - - finish() - } - } diff --git a/ios/MullvadVPN/Operations/ProductsRequestOperation.swift b/ios/MullvadVPN/Operations/ProductsRequestOperation.swift index 953fc28dab..ea1269c49b 100644 --- a/ios/MullvadVPN/Operations/ProductsRequestOperation.swift +++ b/ios/MullvadVPN/Operations/ProductsRequestOperation.swift @@ -9,9 +9,8 @@ import Foundation import StoreKit -class ProductsRequestOperation: AsyncOperation, SKProductsRequestDelegate { +class ProductsRequestOperation: ResultOperation<SKProductsResponse, Error>, SKProductsRequestDelegate { private let productIdentifiers: Set<String> - private var completionHandler: ((OperationCompletion<SKProductsResponse, Error>) -> Void)? private let maxRetryCount = 10 private let retryDelay: DispatchTimeInterval = .seconds(2) @@ -20,11 +19,10 @@ class ProductsRequestOperation: AsyncOperation, SKProductsRequestDelegate { private var retryTimer: DispatchSourceTimer? private var request: SKProductsRequest? - init(productIdentifiers: Set<String>, completionHandler: @escaping (OperationCompletion<SKProductsResponse, Error>) -> Void) { + init(productIdentifiers: Set<String>, completionHandler: @escaping CompletionHandler) { self.productIdentifiers = productIdentifiers - self.completionHandler = completionHandler - super.init() + super.init(completionQueue: .main, completionHandler: completionHandler) } override func main() { @@ -65,9 +63,7 @@ class ProductsRequestOperation: AsyncOperation, SKProductsRequestDelegate { } func productsRequest(_ request: SKProductsRequest, didReceive response: SKProductsResponse) { - DispatchQueue.main.async { - self.finish(completion: .success(response)) - } + finish(completion: .success(response)) } // MARK: - Private @@ -92,13 +88,4 @@ class ProductsRequestOperation: AsyncOperation, SKProductsRequestDelegate { retryTimer?.schedule(wallDeadline: .now() + self.retryDelay) retryTimer?.activate() } - - private func finish(completion: OperationCompletion<SKProductsResponse, Error>) { - assert(Thread.isMainThread) - - completionHandler?(completion) - completionHandler = nil - - finish() - } } diff --git a/ios/MullvadVPN/RelayCache/RelayCacheTracker.swift b/ios/MullvadVPN/RelayCache/RelayCacheTracker.swift index b5e9d87a44..9495c0adeb 100644 --- a/ios/MullvadVPN/RelayCache/RelayCacheTracker.swift +++ b/ios/MullvadVPN/RelayCache/RelayCacheTracker.swift @@ -303,9 +303,8 @@ extension RelayCache.Tracker { } } -fileprivate class UpdateRelaysOperation: AsyncOperation { +fileprivate class UpdateRelaysOperation: ResultOperation<RelayCache.FetchResult, RelayCache.Error> { typealias UpdateHandler = (RelayCache.CachedRelays) -> Void - typealias CompletionHandler = (OperationCompletion<RelayCache.FetchResult, RelayCache.Error>) -> Void private let dispatchQueue: DispatchQueue private let restClient: REST.Client @@ -315,7 +314,6 @@ fileprivate class UpdateRelaysOperation: AsyncOperation { private let logger = Logger(label: "RelayCacheTracker.UpdateRelaysOperation") private let updateHandler: UpdateHandler - private var completionHandler: CompletionHandler? private var downloadCancellable: Cancellable? init(dispatchQueue: DispatchQueue, @@ -329,7 +327,8 @@ fileprivate class UpdateRelaysOperation: AsyncOperation { self.cacheFileURL = cacheFileURL self.relayUpdateInterval = relayUpdateInterval self.updateHandler = updateHandler - self.completionHandler = completionHandler + + super.init(completionQueue: dispatchQueue, completionHandler: completionHandler) } override func main() { @@ -370,15 +369,6 @@ fileprivate class UpdateRelaysOperation: AsyncOperation { } } - private func finish(completion: OperationCompletion<RelayCache.FetchResult, RelayCache.Error>) { - let block = completionHandler - completionHandler = nil - - block?(completion) - - finish() - } - private func didReceiveNewRelays(etag: String?, relays: REST.ServerRelaysResponse) { let numRelays = relays.wireguard.relays.count diff --git a/ios/MullvadVPN/TunnelIPC/TunnelIPCRequestOperation.swift b/ios/MullvadVPN/TunnelIPC/TunnelIPCRequestOperation.swift index c634b679e6..cb48e414d7 100644 --- a/ios/MullvadVPN/TunnelIPC/TunnelIPCRequestOperation.swift +++ b/ios/MullvadVPN/TunnelIPC/TunnelIPCRequestOperation.swift @@ -20,9 +20,8 @@ extension TunnelIPC { var timeout: TimeInterval = 5 } - final class RequestOperation<Output>: AsyncOperation { + final class RequestOperation<Output>: ResultOperation<Output, TunnelIPC.Error> { typealias DecoderHandler = (Data?) -> Result<Output, TunnelIPC.Error> - typealias CompletionHandler = (OperationCompletion<Output, TunnelIPC.Error>) -> Void private let queue: DispatchQueue @@ -31,7 +30,6 @@ extension TunnelIPC { private let options: RequestOptions private let decoderHandler: DecoderHandler - private var completionHandler: CompletionHandler? private var statusObserver: Tunnel.StatusBlockObserver? private var timeoutWork: DispatchWorkItem? @@ -53,12 +51,24 @@ extension TunnelIPC { self.options = options self.decoderHandler = decoderHandler - self.completionHandler = completionHandler + + super.init(completionQueue: queue, completionHandler: completionHandler) } override func main() { queue.async { - self.execute() + guard !self.isCancelled else { + self.completeOperation(completion: .cancelled) + return + } + + self.setTimeoutTimer(connectingStateWaitDelay: 0) + + self.statusObserver = self.tunnel.addBlockObserver(queue: self.queue) { [weak self] tunnel, status in + self?.handleVPNStatus(status) + } + + self.handleVPNStatus(self.tunnel.status) } } @@ -72,21 +82,6 @@ extension TunnelIPC { } } - private func execute() { - guard !isCancelled else { - completeOperation(completion: .cancelled) - return - } - - setTimeoutTimer(connectingStateWaitDelay: 0) - - statusObserver = tunnel.addBlockObserver(queue: queue) { [weak self] tunnel, status in - self?.handleVPNStatus(status) - } - - handleVPNStatus(tunnel.status) - } - private func removeVPNStatusObserver() { statusObserver?.invalidate() statusObserver = nil @@ -211,12 +206,8 @@ extension TunnelIPC { timeoutWork?.cancel() waitForConnectingStateWork?.cancel() - // Call completion handler. - completionHandler?(completion) - completionHandler = nil - // Finish operation. - finish() + finish(completion: completion) } } } diff --git a/ios/MullvadVPN/TunnelManager/LoadTunnelOperation.swift b/ios/MullvadVPN/TunnelManager/LoadTunnelOperation.swift index fc393b7494..83946f02bf 100644 --- a/ios/MullvadVPN/TunnelManager/LoadTunnelOperation.swift +++ b/ios/MullvadVPN/TunnelManager/LoadTunnelOperation.swift @@ -9,13 +9,10 @@ import Foundation import Logging -class LoadTunnelOperation: AsyncOperation { - typealias CompletionHandler = (OperationCompletion<(), TunnelManager.Error>) -> Void - +class LoadTunnelOperation: ResultOperation<(), TunnelManager.Error> { private let queue: DispatchQueue private let accountToken: String? private let state: TunnelManager.State - private var completionHandler: CompletionHandler? private let logger = Logger(label: "TunnelManager.LoadTunnelOperation") @@ -23,16 +20,14 @@ class LoadTunnelOperation: AsyncOperation { self.queue = queue self.state = state self.accountToken = accountToken - self.completionHandler = completionHandler + + super.init(completionQueue: queue, completionHandler: completionHandler) } override func main() { queue.async { self.execute { completion in - self.completionHandler?(completion) - self.completionHandler = nil - - self.finish() + self.finish(completion: completion) } } } diff --git a/ios/MullvadVPN/TunnelManager/ReloadTunnelOperation.swift b/ios/MullvadVPN/TunnelManager/ReloadTunnelOperation.swift index 090a29f59f..48308b7e77 100644 --- a/ios/MullvadVPN/TunnelManager/ReloadTunnelOperation.swift +++ b/ios/MullvadVPN/TunnelManager/ReloadTunnelOperation.swift @@ -8,39 +8,37 @@ import Foundation -class ReloadTunnelOperation: AsyncOperation { - typealias CompletionHandler = (OperationCompletion<(), TunnelManager.Error>) -> Void - +class ReloadTunnelOperation: ResultOperation<(), TunnelManager.Error> { private let queue: DispatchQueue private let state: TunnelManager.State - private var request: Cancellable? - private var completionHandler: CompletionHandler? + private var cancellableTask: Cancellable? init(queue: DispatchQueue, state: TunnelManager.State, completionHandler: @escaping CompletionHandler) { self.queue = queue self.state = state - self.completionHandler = completionHandler + + super.init(completionQueue: queue, completionHandler: completionHandler) } override func main() { queue.async { guard !self.isCancelled else { - self.completeOperation(completion: .cancelled) + self.finish(completion: .cancelled) return } guard let tunnel = self.state.tunnel else { - self.completeOperation(completion: .failure(.unsetAccount)) + self.finish(completion: .failure(.unsetAccount)) return } let session = TunnelIPC.Session(tunnel: tunnel) - self.request = session.reloadTunnelSettings { [weak self] completion in + self.cancellableTask = session.reloadTunnelSettings { [weak self] completion in guard let self = self else { return } self.queue.async { - self.completeOperation(completion: completion.mapError { .reloadTunnel($0) }) + self.finish(completion: completion.mapError { .reloadTunnel($0) }) } } } @@ -50,15 +48,8 @@ class ReloadTunnelOperation: AsyncOperation { super.cancel() queue.async { - self.request?.cancel() + self.cancellableTask?.cancel() + self.cancellableTask = nil } } - - private func completeOperation(completion: OperationCompletion<(), TunnelManager.Error>) { - completionHandler?(completion) - completionHandler = nil - - finish() - } - } diff --git a/ios/MullvadVPN/TunnelManager/ReplaceKeyOperation.swift b/ios/MullvadVPN/TunnelManager/ReplaceKeyOperation.swift index 7d610c5e6f..ff4849e741 100644 --- a/ios/MullvadVPN/TunnelManager/ReplaceKeyOperation.swift +++ b/ios/MullvadVPN/TunnelManager/ReplaceKeyOperation.swift @@ -9,9 +9,7 @@ import Foundation import Logging -class ReplaceKeyOperation: AsyncOperation { - typealias CompletionHandler = (OperationCompletion<TunnelManager.KeyRotationResult, TunnelManager.Error>) -> Void - +class ReplaceKeyOperation: ResultOperation<TunnelManager.KeyRotationResult, TunnelManager.Error> { private let queue: DispatchQueue private let state: TunnelManager.State @@ -77,16 +75,13 @@ class ReplaceKeyOperation: AsyncOperation { self.restClient = restClient self.rotationInterval = rotationInterval - self.completionHandler = completionHandler + super.init(completionQueue: queue, completionHandler: completionHandler) } override func main() { queue.async { self.execute { completion in - self.completionHandler?(completion) - self.completionHandler = nil - - self.finish() + self.finish(completion: completion) } } } diff --git a/ios/MullvadVPN/TunnelManager/SetAccountOperation.swift b/ios/MullvadVPN/TunnelManager/SetAccountOperation.swift index 90b7cd3779..228bdee460 100644 --- a/ios/MullvadVPN/TunnelManager/SetAccountOperation.swift +++ b/ios/MullvadVPN/TunnelManager/SetAccountOperation.swift @@ -10,9 +10,8 @@ import Foundation import class WireGuardKitTypes.PublicKey import Logging -class SetAccountOperation: AsyncOperation { +class SetAccountOperation: ResultOperation<(), TunnelManager.Error> { typealias WillDeleteVPNConfigurationHandler = () -> Void - typealias CompletionHandler = (OperationCompletion<(), TunnelManager.Error>) -> Void private let queue: DispatchQueue private let state: TunnelManager.State @@ -20,8 +19,6 @@ class SetAccountOperation: AsyncOperation { private let accountToken: String? private var willDeleteVPNConfigurationHandler: WillDeleteVPNConfigurationHandler? - private var completionHandler: CompletionHandler? - private let logger = Logger(label: "TunnelManager.SetAccountOperation") init(queue: DispatchQueue, state: TunnelManager.State, restClient: REST.Client, accountToken: String?, willDeleteVPNConfigurationHandler: @escaping WillDeleteVPNConfigurationHandler, completionHandler: @escaping CompletionHandler) { @@ -30,16 +27,14 @@ class SetAccountOperation: AsyncOperation { self.restClient = restClient self.accountToken = accountToken self.willDeleteVPNConfigurationHandler = willDeleteVPNConfigurationHandler - self.completionHandler = completionHandler + + super.init(completionQueue: queue, completionHandler: completionHandler) } override func main() { queue.async { self.execute { completion in - self.completionHandler?(completion) - self.completionHandler = nil - - self.finish() + self.finish(completion: completion) } } } diff --git a/ios/MullvadVPN/TunnelManager/SetTunnelSettingsOperation.swift b/ios/MullvadVPN/TunnelManager/SetTunnelSettingsOperation.swift index 86e6c1a4ff..e818474f60 100644 --- a/ios/MullvadVPN/TunnelManager/SetTunnelSettingsOperation.swift +++ b/ios/MullvadVPN/TunnelManager/SetTunnelSettingsOperation.swift @@ -8,57 +8,45 @@ import Foundation -class SetTunnelSettingsOperation: AsyncOperation { +class SetTunnelSettingsOperation: ResultOperation<(), TunnelManager.Error> { typealias ModificationHandler = (inout TunnelSettings) -> Void - typealias CompletionHandler = (OperationCompletion<(), TunnelManager.Error>) -> Void private let queue: DispatchQueue private let state: TunnelManager.State private let modificationBlock: ModificationHandler - private var completionHandler: CompletionHandler? init(queue: DispatchQueue, state: TunnelManager.State, modificationBlock: @escaping ModificationHandler, completionHandler: @escaping CompletionHandler) { self.queue = queue self.state = state self.modificationBlock = modificationBlock - self.completionHandler = completionHandler + + super.init(completionQueue: queue, completionHandler: completionHandler) } override func main() { queue.async { - self.execute { completion in - self.completionHandler?(completion) - self.completionHandler = nil - - self.finish() + guard !self.isCancelled else { + self.finish(completion: .cancelled) + return } - } - } - private func execute(completionHandler: CompletionHandler) { - guard !isCancelled else { - completionHandler(.cancelled) - return - } - - guard let accountToken = state.tunnelInfo?.token else { - completionHandler(.failure(.unsetAccount)) - return - } - - let result = TunnelSettingsManager.update(searchTerm: .accountToken(accountToken)) { tunnelSettings in - self.modificationBlock(&tunnelSettings) - } - - switch result { - case .success(let newTunnelSettings): - state.tunnelInfo?.tunnelSettings = newTunnelSettings + guard let accountToken = self.state.tunnelInfo?.token else { + self.finish(completion: .failure(.unsetAccount)) + return + } - completionHandler(.success(())) + let result = TunnelSettingsManager.update(searchTerm: .accountToken(accountToken)) { tunnelSettings in + self.modificationBlock(&tunnelSettings) + } - case .failure(let error): - completionHandler(.failure(.updateTunnelSettings(error))) + switch result { + case .success(let newTunnelSettings): + self.state.tunnelInfo?.tunnelSettings = newTunnelSettings + self.finish(completion: .success(())) + case .failure(let error): + self.finish(completion: .failure(.updateTunnelSettings(error))) + } } } } diff --git a/ios/MullvadVPN/TunnelManager/StartTunnelOperation.swift b/ios/MullvadVPN/TunnelManager/StartTunnelOperation.swift index ac8223f963..43bcb9241b 100644 --- a/ios/MullvadVPN/TunnelManager/StartTunnelOperation.swift +++ b/ios/MullvadVPN/TunnelManager/StartTunnelOperation.swift @@ -9,82 +9,71 @@ import Foundation import NetworkExtension -class StartTunnelOperation: AsyncOperation { +class StartTunnelOperation: ResultOperation<(), TunnelManager.Error> { typealias EncodeErrorHandler = (Error) -> Void - typealias CompletionHandler = (OperationCompletion<(), TunnelManager.Error>) -> Void private let queue: DispatchQueue private let state: TunnelManager.State private var encodeErrorHandler: EncodeErrorHandler? - private var completionHandler: CompletionHandler? init(queue: DispatchQueue, state: TunnelManager.State, encodeErrorHandler: @escaping EncodeErrorHandler, completionHandler: @escaping CompletionHandler) { self.queue = queue self.state = state self.encodeErrorHandler = encodeErrorHandler - self.completionHandler = completionHandler + + super.init(completionQueue: queue, completionHandler: completionHandler) } override func main() { queue.async { - self.execute { completion in - self.completionHandler?(completion) - self.completionHandler = nil - - self.finish() + guard !self.isCancelled else { + self.finish(completion: .cancelled) + return } - } - } - - private func execute(completionHandler: @escaping CompletionHandler) { - guard !self.isCancelled else { - completionHandler(.cancelled) - return - } - guard let tunnelInfo = self.state.tunnelInfo else { - completionHandler(.failure(.unsetAccount)) - return - } + guard let tunnelInfo = self.state.tunnelInfo else { + self.finish(completion: .failure(.unsetAccount)) + return + } - switch self.state.tunnelStatus.state { - case .disconnecting(.nothing): - self.state.tunnelStatus.state = .disconnecting(.reconnect) + switch self.state.tunnelStatus.state { + case .disconnecting(.nothing): + self.state.tunnelStatus.state = .disconnecting(.reconnect) - completionHandler(.success(())) + self.finish(completion: .success(())) - case .disconnected, .pendingReconnect: - RelayCache.Tracker.shared.read { readResult in - self.queue.async { - switch readResult { - case .success(let cachedRelays): - self.didReceiveRelays( - tunnelInfo: tunnelInfo, - cachedRelays: cachedRelays, - completionHandler: completionHandler - ) + case .disconnected, .pendingReconnect: + RelayCache.Tracker.shared.read { readResult in + self.queue.async { + switch readResult { + case .success(let cachedRelays): + self.didReceiveRelays( + tunnelInfo: tunnelInfo, + cachedRelays: cachedRelays + ) - case .failure(let error): - completionHandler(.failure(.readRelays(error))) + case .failure(let error): + self.finish(completion: .failure(.readRelays(error))) + } } } - } - default: - // Do not attempt to start the tunnel in all other cases. - completionHandler(.success(())) + default: + // Do not attempt to start the tunnel in all other cases. + self.finish(completion: .success(())) + } } } - private func didReceiveRelays(tunnelInfo: TunnelInfo, cachedRelays: RelayCache.CachedRelays, completionHandler: @escaping (OperationCompletion<(), TunnelManager.Error>) -> Void) { + private func didReceiveRelays(tunnelInfo: TunnelInfo, cachedRelays: RelayCache.CachedRelays) { let selectorResult = RelaySelector.evaluate( relays: cachedRelays.relays, constraints: tunnelInfo.tunnelSettings.relayConstraints ) guard let selectorResult = selectorResult else { - completionHandler(.failure(.cannotSatisfyRelayConstraints)) + finish(completion: .failure(.cannotSatisfyRelayConstraints)) return } @@ -94,10 +83,10 @@ class StartTunnelOperation: AsyncOperation { case .success(let tunnelProvider): let startTunnelResult = Result { try self.startTunnel(tunnelProvider: tunnelProvider, selectorResult: selectorResult) } - completionHandler(OperationCompletion(result: startTunnelResult.mapError { .startVPNTunnel($0) })) + self.finish(completion: OperationCompletion(result: startTunnelResult.mapError { .startVPNTunnel($0) })) case .failure(let error): - completionHandler(.failure(error)) + self.finish(completion: .failure(error)) } } } diff --git a/ios/MullvadVPN/TunnelManager/StopTunnelOperation.swift b/ios/MullvadVPN/TunnelManager/StopTunnelOperation.swift index 377d946399..39b6833716 100644 --- a/ios/MullvadVPN/TunnelManager/StopTunnelOperation.swift +++ b/ios/MullvadVPN/TunnelManager/StopTunnelOperation.swift @@ -8,64 +8,53 @@ import Foundation -class StopTunnelOperation: AsyncOperation { - typealias CompletionHandler = (OperationCompletion<(), TunnelManager.Error>) -> Void - +class StopTunnelOperation: ResultOperation<(), TunnelManager.Error> { private let queue: DispatchQueue private let state: TunnelManager.State - private var completionHandler: CompletionHandler? init(queue: DispatchQueue, state: TunnelManager.State, completionHandler: @escaping CompletionHandler) { self.queue = queue self.state = state - self.completionHandler = completionHandler + + super.init(completionQueue: queue, completionHandler: completionHandler) } override func main() { queue.async { - self.execute { completion in - self.completionHandler?(completion) - self.completionHandler = nil - - self.finish() + guard !self.isCancelled else { + self.finish(completion: .cancelled) + return } - } - } - private func execute(completionHandler: @escaping CompletionHandler) { - guard !isCancelled else { - completionHandler(.cancelled) - return - } - - guard let tunnel = state.tunnel else { - completionHandler(.failure(.unsetAccount)) - return - } + guard let tunnel = self.state.tunnel else { + self.finish(completion: .failure(.unsetAccount)) + return + } - switch self.state.tunnelStatus.state { - case .disconnecting(.reconnect): - state.tunnelStatus.state = .disconnecting(.nothing) + switch self.state.tunnelStatus.state { + case .disconnecting(.reconnect): + self.state.tunnelStatus.state = .disconnecting(.nothing) - completionHandler(.success(())) + self.finish(completion: .success(())) - case .connected, .connecting, .reconnecting: - // Disable on-demand when stopping the tunnel to prevent it from coming back up - tunnel.isOnDemandEnabled = false + case .connected, .connecting, .reconnecting: + // Disable on-demand when stopping the tunnel to prevent it from coming back up + tunnel.isOnDemandEnabled = false - tunnel.saveToPreferences { error in - self.queue.async { - if let error = error { - completionHandler(.failure(.saveVPNConfiguration(error))) - } else { - tunnel.stop() - completionHandler(.success(())) + tunnel.saveToPreferences { error in + self.queue.async { + if let error = error { + self.finish(completion: .failure(.saveVPNConfiguration(error))) + } else { + tunnel.stop() + self.finish(completion: .success(())) + } } } - } - default: - completionHandler(.success(())) + default: + self.finish(completion: .success(())) + } } } } |
