135 lines
4.3 KiB
Swift
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)
|
|
}
|
|
|
|
}
|