import UIKit import Models import Shared import Combine import Defaults import Foundation import Integration import BackupFeature import DependencyInjection import SFTPFeature import iCloudFeature import DropboxFeature import GoogleDriveFeature enum RestorationStep { case idle(CloudService, Backup?) case downloading(Float, Float) case failDownload(Error) case wrongPass case parsingData case done } extension RestorationStep: Equatable { static func ==(lhs: RestorationStep, rhs: RestorationStep) -> Bool { switch (lhs, rhs) { case (.done, .done), (.wrongPass, .wrongPass): return true case let (.failDownload(a), .failDownload(b)): return a.localizedDescription == b.localizedDescription case let (.downloading(a, b), .downloading(c, d)): return a == c && b == d case (.idle, _), (.downloading, _), (.parsingData, _), (.done, _), (.failDownload, _), (.wrongPass, _): return false } } } final class RestoreViewModel { @Dependency private var sftpService: SFTPService @Dependency private var iCloudService: iCloudInterface @Dependency private var dropboxService: DropboxInterface @Dependency private var googleService: GoogleDriveInterface @KeyObject(.username, defaultValue: nil) var username: String? var step: AnyPublisher<RestorationStep, Never> { stepRelay.eraseToAnyPublisher() } // TO REFACTOR: // private var pendingData: Data? private let ndf: String private var passphrase: String! private let settings: RestoreSettings private let stepRelay: CurrentValueSubject<RestorationStep, Never> init(ndf: String, settings: RestoreSettings) { self.ndf = ndf self.settings = settings self.stepRelay = .init(.idle(settings.cloudService, settings.backup)) } func retryWith(passphrase: String) { self.passphrase = passphrase continueRestoring(data: pendingData!) } func didTapRestore(passphrase: String) { self.passphrase = passphrase guard let backup = settings.backup else { fatalError() } stepRelay.send(.downloading(0.0, backup.size)) switch settings.cloudService { case .drive: downloadBackupForDrive(backup) case .dropbox: downloadBackupForDropbox(backup) case .icloud: downloadBackupForiCloud(backup) case .sftp: downloadBackupForSFTP(backup) } } private func downloadBackupForSFTP(_ backup: Backup) { do { try sftpService.downloadBackup(backup.id) } catch { print(error.localizedDescription) } } private func downloadBackupForDropbox(_ backup: Backup) { dropboxService.downloadBackup(backup.id) { [weak self] in guard let self = self else { return } self.stepRelay.send(.downloading(backup.size, backup.size)) switch $0 { case .success(let data): self.continueRestoring(data: data) case .failure(let error): self.stepRelay.send(.failDownload(error)) } } } private func downloadBackupForiCloud(_ backup: Backup) { iCloudService.downloadBackup(backup.id) { [weak self] in guard let self = self else { return } self.stepRelay.send(.downloading(backup.size, backup.size)) switch $0 { case .success(let data): self.continueRestoring(data: data) case .failure(let error): self.stepRelay.send(.failDownload(error)) } } } private func downloadBackupForDrive(_ backup: Backup) { googleService.downloadBackup(backup.id) { [weak self] in if let stepRelay = self?.stepRelay { stepRelay.send(.downloading($0, backup.size)) } } _: { [weak self] in guard let self = self else { return } switch $0 { case .success(let data): self.continueRestoring(data: data) case .failure(let error): self.stepRelay.send(.failDownload(error)) } } } private func continueRestoring(data: Data) { stepRelay.send(.parsingData) DispatchQueue.global().async { [weak self] in guard let self = self else { return } do { let session = try Session( passphrase: self.passphrase, backupFile: data, ndf: self.ndf ) DependencyInjection.Container.shared.register(session as SessionType) self.stepRelay.send(.done) } catch { self.pendingData = data self.stepRelay.send(.wrongPass) } } } }