Skip to content

Commit

Permalink
Merge pull request #1 from diegotl/feature/restore-backup
Browse files Browse the repository at this point in the history
Restore backup feature, fix typos, classes renaming and UI adjustments
  • Loading branch information
diegotl authored Oct 18, 2023
2 parents 8080c47 + 431791f commit 4e45794
Show file tree
Hide file tree
Showing 18 changed files with 510 additions and 124 deletions.
14 changes: 7 additions & 7 deletions Empusa.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@
children = (
3F21D3362ADADE8B00B6A5D9 /* Packages */,
3F21D3092ADADC7300B6A5D9 /* Empusa */,
3F21D31B2ADADC7400B6A5D9 /* Empusa Tests */,
3F21D31B2ADADC7400B6A5D9 /* Tests */,
3F21D3082ADADC7300B6A5D9 /* Products */,
3F21D3382ADADED500B6A5D9 /* Frameworks */,
);
Expand All @@ -96,12 +96,12 @@
path = Empusa;
sourceTree = "<group>";
};
3F21D31B2ADADC7400B6A5D9 /* Empusa Tests */ = {
3F21D31B2ADADC7400B6A5D9 /* Tests */ = {
isa = PBXGroup;
children = (
3F21D31C2ADADC7400B6A5D9 /* EmpusaTests.swift */,
);
path = "Empusa Tests";
path = Tests;
sourceTree = "<group>";
};
3F21D3352ADADC8D00B6A5D9 /* Resources */ = {
Expand Down Expand Up @@ -396,7 +396,7 @@
CODE_SIGN_ENTITLEMENTS = Empusa/Empusa.entitlements;
CODE_SIGN_STYLE = Automatic;
COMBINE_HIDPI_IMAGES = YES;
CURRENT_PROJECT_VERSION = 1;
CURRENT_PROJECT_VERSION = 2;
DEVELOPMENT_TEAM = D97MJ3844Q;
ENABLE_HARDENED_RUNTIME = YES;
ENABLE_PREVIEWS = YES;
Expand All @@ -409,7 +409,7 @@
"@executable_path/../Frameworks",
);
MACOSX_DEPLOYMENT_TARGET = 13.0;
MARKETING_VERSION = 1.0;
MARKETING_VERSION = 1.0.1;
PRODUCT_BUNDLE_IDENTIFIER = nl.trevisa.diego.Empusa;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_EMIT_LOC_STRINGS = YES;
Expand All @@ -425,7 +425,7 @@
CODE_SIGN_ENTITLEMENTS = Empusa/Empusa.entitlements;
CODE_SIGN_STYLE = Automatic;
COMBINE_HIDPI_IMAGES = YES;
CURRENT_PROJECT_VERSION = 1;
CURRENT_PROJECT_VERSION = 2;
DEVELOPMENT_TEAM = D97MJ3844Q;
ENABLE_HARDENED_RUNTIME = YES;
ENABLE_PREVIEWS = YES;
Expand All @@ -438,7 +438,7 @@
"@executable_path/../Frameworks",
);
MACOSX_DEPLOYMENT_TARGET = 13.0;
MARKETING_VERSION = 1.0;
MARKETING_VERSION = 1.0.1;
PRODUCT_BUNDLE_IDENTIFIER = nl.trevisa.diego.Empusa;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_EMIT_LOC_STRINGS = YES;
Expand Down
95 changes: 95 additions & 0 deletions Empusa.xcodeproj/xcshareddata/xcschemes/Empusa.xcscheme
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "1500"
version = "1.7">
<BuildAction
parallelizeBuildables = "YES"
buildImplicitDependencies = "YES">
<BuildActionEntries>
<BuildActionEntry
buildForTesting = "YES"
buildForRunning = "YES"
buildForProfiling = "YES"
buildForArchiving = "YES"
buildForAnalyzing = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "3F21D3062ADADC7300B6A5D9"
BuildableName = "Empusa.app"
BlueprintName = "Empusa"
ReferencedContainer = "container:Empusa.xcodeproj">
</BuildableReference>
</BuildActionEntry>
</BuildActionEntries>
</BuildAction>
<TestAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
shouldUseLaunchSchemeArgsEnv = "YES">
<TestPlans>
<TestPlanReference
reference = "container:Empusa Tests/Empusa.xctestplan"
default = "YES">
</TestPlanReference>
</TestPlans>
<Testables>
<TestableReference
skipped = "NO"
parallelizable = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "3F21D3172ADADC7400B6A5D9"
BuildableName = "EmpusaTests.xctest"
BlueprintName = "EmpusaTests"
ReferencedContainer = "container:Empusa.xcodeproj">
</BuildableReference>
</TestableReference>
</Testables>
</TestAction>
<LaunchAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
launchStyle = "0"
useCustomWorkingDirectory = "NO"
ignoresPersistentStateOnLaunch = "NO"
debugDocumentVersioning = "YES"
debugServiceExtension = "internal"
allowLocationSimulation = "YES">
<BuildableProductRunnable
runnableDebuggingMode = "0">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "3F21D3062ADADC7300B6A5D9"
BuildableName = "Empusa.app"
BlueprintName = "Empusa"
ReferencedContainer = "container:Empusa.xcodeproj">
</BuildableReference>
</BuildableProductRunnable>
</LaunchAction>
<ProfileAction
buildConfiguration = "Release"
shouldUseLaunchSchemeArgsEnv = "YES"
savedToolIdentifier = ""
useCustomWorkingDirectory = "NO"
debugDocumentVersioning = "YES">
<BuildableProductRunnable
runnableDebuggingMode = "0">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "3F21D3062ADADC7300B6A5D9"
BuildableName = "Empusa.app"
BlueprintName = "Empusa"
ReferencedContainer = "container:Empusa.xcodeproj">
</BuildableReference>
</BuildableProductRunnable>
</ProfileAction>
<AnalyzeAction
buildConfiguration = "Debug">
</AnalyzeAction>
<ArchiveAction
buildConfiguration = "Release"
revealArchiveInOrganizer = "YES">
</ArchiveAction>
</Scheme>
108 changes: 83 additions & 25 deletions Empusa/EmpusaModel.swift
Original file line number Diff line number Diff line change
Expand Up @@ -10,68 +10,93 @@ struct AlertData {
title = "Error"
message = error.localizedDescription
}

init(title: String, message: String) {
self.title = title
self.message = message
}
}

@MainActor
final class EmpusaModel: ObservableObject {
// MARK: - Public variables
@Published var externalStorages: [ExternalStorage] = []
@Published var selectedExternalStorage: ExternalStorage = .none
@Published var externalVolumes: [ExternalVolume] = []
@Published var selectedVolume: ExternalVolume = .none

@Published var availableResources: [SwitchResource] = SwitchResource.allCases
@Published var isLoadingResources: Bool = false
@Published var availableResources: [DisplayingSwitchResource] = []
@Published var selectedResources: [SwitchResource] = SwitchResource.allCases

@Published var isImporting: Bool = false
@Published var isExporting: Bool = false
@Published var exportingFile: ZipFile?

@Published var isProcessing: Bool = false
@Published var progress: ProgressData?

@Published var isShowingAlert: Bool = false
@Published var isPresentingAlert: Bool = false
@Published var alertData: AlertData? {
didSet {
isShowingAlert = alertData != nil
isPresentingAlert = alertData != nil
}
}

var validVolumeSelected: Bool {
selectedVolume != .none
}

var canStartProcess: Bool {
selectedExternalStorage != .none && !selectedResources.isEmpty && !isProcessing && !isExporting
validVolumeSelected && !selectedResources.isEmpty && !isProcessing && !isExporting
}

lazy var backupCompletion: ((Result<URL, Error>) -> Void) = { [weak self] result in
lazy var exportCompletion: ((Result<URL, Error>) -> Void) = { [weak self] result in
if let tempZipFilePath = self?.exportingFile?.url {
self?.storageService.removeItem(at: tempZipFilePath)
}

self?.exportingFile = nil
self?.isExporting = false
}

lazy var importCompletion: ((Result<URL, Error>) -> Void) = { [weak self] result in
switch result {
case .success(let zipUrl):
self?.restoreBackup(zipUrl: zipUrl)
case .failure(let error):
self?.alertData = .init(error: error)
}
}

// MARK: - Dependencies
private let storageService: StorageServiceProtocol = StorageService()
let contentManager: ContentManagerProtocol = ContentManager()

private let assetService: AssetServiceProtocol = AssetService()
private let resourceService: ResourceServiceProtocol = ResourceService()
private let contentManager: ContentManagerProtocol = ContentManager()
private let logger: Logger = .init(subsystem: "nl.trevisa.diego.Empusa", category: "EmpusaModel")

// MARK: - Init
init() {
loadExternalStorages()
loadExternalVolumes()
loadResourcesVersions()
}

// MARK: - Public functions
func loadExternalStorages() {
func loadExternalVolumes() {
do {
let externalStorages = try storageService.listStorages()
self.externalStorages = externalStorages
externalVolumes = try storageService.listExternalVolumes()

if selectedExternalStorage == .none || !externalStorages.contains(selectedExternalStorage) {
selectedExternalStorage = externalStorages.last ?? .none
if selectedVolume == .none || !externalVolumes.contains(selectedVolume) {
selectedVolume = externalVolumes.last ?? .none
}
} catch {
logger.error("Failed to load external storages: \(error.localizedDescription)")
selectedExternalStorage = .none
logger.error("Failed to load external volumes: \(error.localizedDescription)")
selectedVolume = .none
alertData = .init(error: error)
}
}

func execute() {
guard selectedExternalStorage != .none else { return }
guard validVolumeSelected else { return }

Task(priority: .background) { [weak self] in
guard let self else { return }
Expand All @@ -85,7 +110,7 @@ final class EmpusaModel: ObservableObject {
do {
try await contentManager.download(
resources: selectedResources,
into: selectedExternalStorage.url,
into: selectedVolume.url,
progressSubject: progressSubject
)
} catch {
Expand All @@ -98,30 +123,63 @@ final class EmpusaModel: ObservableObject {
}

func backup() {
guard selectedExternalStorage != .none else { return }
guard validVolumeSelected else { return }

Task(priority: .background) { [weak self] in
guard let self else { return }
isProcessing = true

let progressSubject = CurrentValueSubject<ProgressData?, Never>(nil)
progressSubject
.receive(on: RunLoop.main)
.assign(to: &$progress)

let zipFile = try await contentManager.backupStorage(
at: selectedExternalStorage.url,
let zipFile = try await contentManager.backupVolume(
at: selectedVolume.url,
progressSubject: progressSubject
)

isProcessing = false
exportingFile = zipFile
isExporting = true
self.progress = nil
}
}

private func report(progressData: ProgressData) {
Task { @MainActor in
self.progress = progressData
func restoreBackup(zipUrl: URL) {
guard validVolumeSelected else { return }
isProcessing = true

Task(priority: .background) { [weak self] in
guard let self else { return }

let progressSubject = CurrentValueSubject<ProgressData?, Never>(nil)
progressSubject
.receive(on: RunLoop.main)
.assign(to: &$progress)

try await contentManager
.restoreBackup(
at: zipUrl,
to: selectedVolume.url,
progressSubject: progressSubject
)

isProcessing = false
self.progress = nil

self.alertData = .init(
title: "Success",
message: "Backup sucessfully restored to the selected volume"
)
}
}

func loadResourcesVersions() {
Task { [resourceService, weak self] in
self?.isLoadingResources = true
self?.availableResources = await resourceService.fetchResources()
self?.isLoadingResources = false
}
}
}
Loading

0 comments on commit 4e45794

Please sign in to comment.