Files
Juraldinio daab1a6375 Show traffic and counting it.
Few fixes with API working.
2021-04-29 00:30:23 +03:00

135 lines
4.3 KiB
Swift

//
// MainServerConveyor.swift
// PrivadoVPN
//
// Created by Juraldinio on 3/24/21.
// Copyright © 2021 Privado LLC. All rights reserved.
//
import Foundation
final class ServerFetcherConveyor {
enum ConveyorError: Error {
case initializing
case cancelled
case destroyed
}
enum State {
case fetch
case convert
case ping
}
typealias StateChangeClosure = (State, Bool) -> Void
typealias ProgressClosure = (Double) -> Void
private let operationQueue = OperationQueue()
private var observation: NSKeyValueObservation?
private let interactor: MainInteractorInput
var stateChangeClosure: StateChangeClosure?
var pingProgress: ProgressClosure?
private(set) var result: MainOperationsResult?
private var completionFuture: Future<MainOperationsResult>?
init(interactor: MainInteractorInput) {
self.interactor = interactor
self.configureQueue(self.operationQueue)
self.configureOperations(for: self.operationQueue)
}
@discardableResult
func run() -> Future<MainOperationsResult> {
guard let future = self.completionFuture else { return Future(error: ConveyorError.initializing) }
// Do not start twice
guard self.operationQueue.isSuspended else { return future }
self.operationQueue.isSuspended = false
return future
}
func cancel() {
guard !self.operationQueue.isSuspended else { return }
self.operationQueue.cancelAllOperations()
self.operationQueue.isSuspended = true
}
// MARK: - Private
private func configureQueue(_ queue: OperationQueue) {
queue.maxConcurrentOperationCount = 2
queue.isSuspended = true
self.completionFuture = Future<MainOperationsResult> { [weak self, weak queue] completion in
guard let self = self
, let queue = queue else {
completion(.failure(ConveyorError.destroyed))
return
}
self.observation = queue.whenEmptyOperations { [weak self] _ in
self?.observation = nil
guard let self = self else {
completion(.failure(ConveyorError.destroyed))
return
}
guard let result = self.result else {
completion(.failure(ConveyorError.cancelled))
return
}
completion(.success(result))
}
}
}
private func configureOperations(for queue: OperationQueue) {
// FetchingServersOperation
let fetchOperation = FetchingServersOperation(interactor: self.interactor)
fetchOperation.completionBlock = { [weak fetchOperation, weak self] in
self?.stateChangeClosure?(.fetch, fetchOperation?.result?.isSuccess ?? false)
}
queue.addOperation(fetchOperation)
// ConvertingServersOperation
let convertOperation = ConvertingServersOperation(interactor: self.interactor)
convertOperation.addDependency(fetchOperation)
convertOperation.completionBlock = { [weak convertOperation, weak self] in
guard let self = self else { return }
guard let operation = convertOperation else {
self.stateChangeClosure?(.convert, false)
return
}
self.result = operation.result
self.stateChangeClosure?(.convert, self.result?.serverRecords.isExist ?? false)
}
queue.addOperation(convertOperation)
// PingServersOperation
let pingServersOperaion = PingServersOperation(progressClosure: self.handlePingProgress)
pingServersOperaion.addDependency(convertOperation)
pingServersOperaion.completionBlock = { [weak self] in self?.stateChangeClosure?(.ping, true)}
self.operationQueue.addOperation(pingServersOperaion)
}
private func handlePingProgress(_ progress: Double) {
guard let closure = self.pingProgress else { return }
closure(progress)
}
}