From af54e63e0e6a72515ef416701bf8d9866de794de Mon Sep 17 00:00:00 2001 From: Diego Trevisan Lara <34442011+diegotl@users.noreply.github.com> Date: Thu, 19 Oct 2023 17:19:00 +0200 Subject: [PATCH] Fix slow download speed and add more resources. (#2) * Fix slow download speed and add more resoources. * Check to update function --- Empusa.xcodeproj/project.pbxproj | 38 +++++++++- .../xcshareddata/swiftpm/Package.resolved | 9 +++ Empusa/EmpusaApp.swift | 15 +++- Empusa/EmpusaModel.swift | 7 +- Empusa/Resources/Info.plist | 10 +++ .../UI Components/CheckForUpdatesView.swift | 26 +++++++ .../Sources/EmpusaKit/Client/Client.swift | 32 ++++---- .../Sources/EmpusaKit/ContentManager.swift | 27 ++++--- .../FileManager+FoldersMerger.swift | 10 +-- .../EmpusaKit/Models/SwitchResource.swift | 68 ++++++++--------- .../EmpusaKit/Services/AssetService.swift | 4 +- .../EmpusaKit/Services/StorageService.swift | 76 ++++++++++++++++--- 12 files changed, 234 insertions(+), 88 deletions(-) create mode 100644 Empusa/Resources/Info.plist create mode 100644 Empusa/UI Components/CheckForUpdatesView.swift diff --git a/Empusa.xcodeproj/project.pbxproj b/Empusa.xcodeproj/project.pbxproj index 2e518d1..7d077be 100644 --- a/Empusa.xcodeproj/project.pbxproj +++ b/Empusa.xcodeproj/project.pbxproj @@ -17,6 +17,8 @@ 3F3B176E2ADDE117000283AE /* DestinationView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3F3B176D2ADDE117000283AE /* DestinationView.swift */; }; 3F3B17702ADDE16A000283AE /* ResourcesView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3F3B176F2ADDE16A000283AE /* ResourcesView.swift */; }; 3F3B17722ADDE25D000283AE /* DataProgressView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3F3B17712ADDE25D000283AE /* DataProgressView.swift */; }; + 3FCE9D2D2AE0944C00A4E3F5 /* Sparkle in Frameworks */ = {isa = PBXBuildFile; productRef = 3FCE9D2C2AE0944C00A4E3F5 /* Sparkle */; }; + 3FCE9D312AE17F5F00A4E3F5 /* CheckForUpdatesView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3FCE9D302AE17F5F00A4E3F5 /* CheckForUpdatesView.swift */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ @@ -43,6 +45,8 @@ 3F3B176D2ADDE117000283AE /* DestinationView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DestinationView.swift; sourceTree = ""; }; 3F3B176F2ADDE16A000283AE /* ResourcesView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ResourcesView.swift; sourceTree = ""; }; 3F3B17712ADDE25D000283AE /* DataProgressView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DataProgressView.swift; sourceTree = ""; }; + 3FCE9D2E2AE0FE2E00A4E3F5 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + 3FCE9D302AE17F5F00A4E3F5 /* CheckForUpdatesView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CheckForUpdatesView.swift; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -51,6 +55,7 @@ buildActionMask = 2147483647; files = ( 3F21D3412ADD77E700B6A5D9 /* EmpusaKit in Frameworks */, + 3FCE9D2D2AE0944C00A4E3F5 /* Sparkle in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -107,6 +112,7 @@ 3F21D3352ADADC8D00B6A5D9 /* Resources */ = { isa = PBXGroup; children = ( + 3FCE9D2E2AE0FE2E00A4E3F5 /* Info.plist */, 3F21D30E2ADADC7400B6A5D9 /* Assets.xcassets */, ); path = Resources; @@ -135,6 +141,7 @@ 3F3B176D2ADDE117000283AE /* DestinationView.swift */, 3F3B176F2ADDE16A000283AE /* ResourcesView.swift */, 3F3B17712ADDE25D000283AE /* DataProgressView.swift */, + 3FCE9D302AE17F5F00A4E3F5 /* CheckForUpdatesView.swift */, ); path = "UI Components"; sourceTree = ""; @@ -157,6 +164,7 @@ name = Empusa; packageProductDependencies = ( 3F21D3402ADD77E700B6A5D9 /* EmpusaKit */, + 3FCE9D2C2AE0944C00A4E3F5 /* Sparkle */, ); productName = SwitchSDTool; productReference = 3F21D3072ADADC7300B6A5D9 /* Empusa.app */; @@ -208,6 +216,9 @@ Base, ); mainGroup = 3F21D2FE2ADADC7300B6A5D9; + packageReferences = ( + 3FCE9D2B2AE0944C00A4E3F5 /* XCRemoteSwiftPackageReference "Sparkle" */, + ); productRefGroup = 3F21D3082ADADC7300B6A5D9 /* Products */; projectDirPath = ""; projectRoot = ""; @@ -243,6 +254,7 @@ files = ( 3F3B17702ADDE16A000283AE /* ResourcesView.swift in Sources */, 3F21D30D2ADADC7300B6A5D9 /* MainView.swift in Sources */, + 3FCE9D312AE17F5F00A4E3F5 /* CheckForUpdatesView.swift in Sources */, 3F3B17722ADDE25D000283AE /* DataProgressView.swift in Sources */, 3F21D30B2ADADC7300B6A5D9 /* EmpusaApp.swift in Sources */, 3F3B176E2ADDE117000283AE /* DestinationView.swift in Sources */, @@ -396,11 +408,12 @@ CODE_SIGN_ENTITLEMENTS = Empusa/Empusa.entitlements; CODE_SIGN_STYLE = Automatic; COMBINE_HIDPI_IMAGES = YES; - CURRENT_PROJECT_VERSION = 2; + CURRENT_PROJECT_VERSION = 3; DEVELOPMENT_TEAM = D97MJ3844Q; ENABLE_HARDENED_RUNTIME = YES; ENABLE_PREVIEWS = YES; GENERATE_INFOPLIST_FILE = YES; + INFOPLIST_FILE = Empusa/Resources/Info.plist; INFOPLIST_KEY_CFBundleDisplayName = "εmpusa"; INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.utilities"; INFOPLIST_KEY_NSHumanReadableCopyright = ""; @@ -409,7 +422,7 @@ "@executable_path/../Frameworks", ); MACOSX_DEPLOYMENT_TARGET = 13.0; - MARKETING_VERSION = 1.0.1; + MARKETING_VERSION = 1.0.2; PRODUCT_BUNDLE_IDENTIFIER = nl.trevisa.diego.Empusa; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_EMIT_LOC_STRINGS = YES; @@ -425,11 +438,12 @@ CODE_SIGN_ENTITLEMENTS = Empusa/Empusa.entitlements; CODE_SIGN_STYLE = Automatic; COMBINE_HIDPI_IMAGES = YES; - CURRENT_PROJECT_VERSION = 2; + CURRENT_PROJECT_VERSION = 3; DEVELOPMENT_TEAM = D97MJ3844Q; ENABLE_HARDENED_RUNTIME = YES; ENABLE_PREVIEWS = YES; GENERATE_INFOPLIST_FILE = YES; + INFOPLIST_FILE = Empusa/Resources/Info.plist; INFOPLIST_KEY_CFBundleDisplayName = "εmpusa"; INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.utilities"; INFOPLIST_KEY_NSHumanReadableCopyright = ""; @@ -438,7 +452,7 @@ "@executable_path/../Frameworks", ); MACOSX_DEPLOYMENT_TARGET = 13.0; - MARKETING_VERSION = 1.0.1; + MARKETING_VERSION = 1.0.2; PRODUCT_BUNDLE_IDENTIFIER = nl.trevisa.diego.Empusa; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_EMIT_LOC_STRINGS = YES; @@ -516,11 +530,27 @@ }; /* End XCConfigurationList section */ +/* Begin XCRemoteSwiftPackageReference section */ + 3FCE9D2B2AE0944C00A4E3F5 /* XCRemoteSwiftPackageReference "Sparkle" */ = { + isa = XCRemoteSwiftPackageReference; + repositoryURL = "https://github.com/sparkle-project/Sparkle"; + requirement = { + kind = upToNextMajorVersion; + minimumVersion = 2.5.1; + }; + }; +/* End XCRemoteSwiftPackageReference section */ + /* Begin XCSwiftPackageProductDependency section */ 3F21D3402ADD77E700B6A5D9 /* EmpusaKit */ = { isa = XCSwiftPackageProductDependency; productName = EmpusaKit; }; + 3FCE9D2C2AE0944C00A4E3F5 /* Sparkle */ = { + isa = XCSwiftPackageProductDependency; + package = 3FCE9D2B2AE0944C00A4E3F5 /* XCRemoteSwiftPackageReference "Sparkle" */; + productName = Sparkle; + }; /* End XCSwiftPackageProductDependency section */ }; rootObject = 3F21D2FF2ADADC7300B6A5D9 /* Project object */; diff --git a/Empusa.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved b/Empusa.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved index 66598a9..cf7e7ee 100644 --- a/Empusa.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved +++ b/Empusa.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -1,5 +1,14 @@ { "pins" : [ + { + "identity" : "sparkle", + "kind" : "remoteSourceControl", + "location" : "https://github.com/sparkle-project/Sparkle", + "state" : { + "revision" : "1f07f4096e52f19b5e7abaa697b7fc592b7ff57c", + "version" : "2.5.1" + } + }, { "identity" : "zip", "kind" : "remoteSourceControl", diff --git a/Empusa/EmpusaApp.swift b/Empusa/EmpusaApp.swift index 06d283b..cc6b5a9 100644 --- a/Empusa/EmpusaApp.swift +++ b/Empusa/EmpusaApp.swift @@ -1,14 +1,27 @@ import SwiftUI +import Sparkle @main struct EmpusaApp: App { + private let updaterController: SPUStandardUpdaterController = .init( + startingUpdater: true, + updaterDelegate: nil, + userDriverDelegate: nil + ) + var body: some Scene { WindowGroup { MainView( model: EmpusaModel() ) - .frame(width: 680, height: 400) + .frame(width: 680, height: 420) } .windowResizability(.contentSize) + .commands { + CommandGroup(replacing: CommandGroupPlacement.newItem) {} + CommandGroup(after: .appInfo) { + CheckForUpdatesView(updater: updaterController.updater) + } + } } } diff --git a/Empusa/EmpusaModel.swift b/Empusa/EmpusaModel.swift index 3258908..6849b0e 100644 --- a/Empusa/EmpusaModel.swift +++ b/Empusa/EmpusaModel.swift @@ -113,6 +113,11 @@ final class EmpusaModel: ObservableObject { into: selectedVolume.url, progressSubject: progressSubject ) + + self.alertData = .init( + title: "Success", + message: "Selected resources have been downloaded into the selected destination." + ) } catch { alertData = .init(error: error) } @@ -170,7 +175,7 @@ final class EmpusaModel: ObservableObject { self.alertData = .init( title: "Success", - message: "Backup sucessfully restored to the selected volume" + message: "Backup sucessfully restored to the selected destination." ) } } diff --git a/Empusa/Resources/Info.plist b/Empusa/Resources/Info.plist new file mode 100644 index 0000000..c2bd41e --- /dev/null +++ b/Empusa/Resources/Info.plist @@ -0,0 +1,10 @@ + + + + + SUFeedURL + https://empusa.pacotevicio.app/sparkle.xml + SUPublicEDKey + 62os1l9hx8vk8noKLn706G/htYbpMlhOP2TTJQFa66w= + + diff --git a/Empusa/UI Components/CheckForUpdatesView.swift b/Empusa/UI Components/CheckForUpdatesView.swift new file mode 100644 index 0000000..7516d76 --- /dev/null +++ b/Empusa/UI Components/CheckForUpdatesView.swift @@ -0,0 +1,26 @@ +import SwiftUI +import Sparkle + +final class CheckForUpdatesViewModel: ObservableObject { + @Published var canCheckForUpdates = false + + init(updater: SPUUpdater) { + updater.publisher(for: \.canCheckForUpdates) + .assign(to: &$canCheckForUpdates) + } +} + +struct CheckForUpdatesView: View { + @ObservedObject private var checkForUpdatesViewModel: CheckForUpdatesViewModel + private let updater: SPUUpdater + + init(updater: SPUUpdater) { + self.updater = updater + self.checkForUpdatesViewModel = CheckForUpdatesViewModel(updater: updater) + } + + var body: some View { + Button("Check for Updates...", action: updater.checkForUpdates) + .disabled(!checkForUpdatesViewModel.canCheckForUpdates) + } +} diff --git a/Packages/EmpusaKit/Sources/EmpusaKit/Client/Client.swift b/Packages/EmpusaKit/Sources/EmpusaKit/Client/Client.swift index 38e3a77..4bc7ce3 100644 --- a/Packages/EmpusaKit/Sources/EmpusaKit/Client/Client.swift +++ b/Packages/EmpusaKit/Sources/EmpusaKit/Client/Client.swift @@ -2,14 +2,17 @@ import Foundation import Combine protocol ClientProtocol { - func request(url: URL) async throws -> T + func request( + url: URL + ) async throws -> T + func downloadFile( url: URL, progressSubject: CurrentValueSubject - ) async throws -> Data + ) async throws -> URL } -final class Client: ClientProtocol { +final class Client: NSObject, ClientProtocol { func request(url: URL) async throws -> T { let request = URLRequest(url: url) let (data, response) = try await URLSession @@ -26,21 +29,16 @@ final class Client: ClientProtocol { func downloadFile( url: URL, progressSubject: CurrentValueSubject - ) async throws -> Data { - let (asyncBytes, urlResponse) = try await URLSession - .shared - .bytes(from: url) - - let length = urlResponse.expectedContentLength - var data = Data() - data.reserveCapacity(Int(length)) - - for try await byte in asyncBytes { - data.append(byte) - let downloadProgress = Double(data.count) / Double(length) - progressSubject.send(downloadProgress) + ) async throws -> URL { + // TODO: implement download progress + defer { + progressSubject.send(1) } - return data + let (localUrl, response) = try await URLSession + .shared + .download(from: url) + + return localUrl } } diff --git a/Packages/EmpusaKit/Sources/EmpusaKit/ContentManager.swift b/Packages/EmpusaKit/Sources/EmpusaKit/ContentManager.swift index 34daad8..72b1ba5 100644 --- a/Packages/EmpusaKit/Sources/EmpusaKit/ContentManager.swift +++ b/Packages/EmpusaKit/Sources/EmpusaKit/ContentManager.swift @@ -42,43 +42,48 @@ public final class ContentManager: ContentManagerProtocol { // Download asset progressTitleSubject.send("Downloading \(resource.assetFileName)...") - let asset = try await githubService.downloadAsset( + let assetFileTempUrl = try await githubService.downloadAsset( for: resource, progressSubject: downloadProgressSubject ) - progressTitleSubject.send("Saving \(resource.assetFileName)...") - let assetFilePath = try await storageService.saveFile( - data: asset, - fileName: resource.assetFileName + // Rename asset file + let assetFileUrl = assetFileTempUrl + .deletingLastPathComponent() + .appending(component: resource.assetFileName) + + try storageService.moveItem( + at: assetFileTempUrl, + to: assetFileUrl ) - let extractedPath = try { + let extractedUrl = try { if resource.isAssetZipped { // Unzip asset progressTitleSubject.send("Unzipping \(resource.assetFileName)...") return try storageService.unzipFile( - at: assetFilePath, + at: assetFileUrl, progressSubject: unzipProgressSubject ) } else { + // Do nothing unzipProgressSubject.send(1) - return assetFilePath + return assetFileUrl } }() // Copy asset to SD progressTitleSubject.send("Copying contents of \(resource.assetFileName) into destination...") try resource.handleAsset( - at: extractedPath, + at: extractedUrl, destination: destination, progressSubject: mergeProgressSubject ) // Delete downloaded files on disk progressTitleSubject.send("Removing temporary files...") - storageService.removeItem(at: assetFilePath) - storageService.removeItem(at: extractedPath) + storageService.removeItem(at: assetFileUrl) + storageService.removeItem(at: extractedUrl) cancellable.cancel() } diff --git a/Packages/EmpusaKit/Sources/EmpusaKit/Extensions/FileManager+FoldersMerger.swift b/Packages/EmpusaKit/Sources/EmpusaKit/Extensions/FileManager+FoldersMerger.swift index 58aefa2..34eaa0c 100644 --- a/Packages/EmpusaKit/Sources/EmpusaKit/Extensions/FileManager+FoldersMerger.swift +++ b/Packages/EmpusaKit/Sources/EmpusaKit/Extensions/FileManager+FoldersMerger.swift @@ -10,11 +10,6 @@ extension FileManager { .init(subsystem: "nl.trevisa.diego.Empusa.Services", category: "FileManager") } - enum ActionType { - case move - case copy - } - enum ConflictResolution { case keepSource case keepDestination @@ -22,7 +17,8 @@ extension FileManager { func moveFile( at location: URL, - to destination: URL + to destination: URL, + progressSubject: CurrentValueSubject ) { let fileName = location.lastPathComponent let filedestination = destination.appending(path: fileName) @@ -50,6 +46,8 @@ extension FileManager { } catch { logger.error("Move file error: \(error.localizedDescription)") } + + progressSubject.send(1) } func merge( diff --git a/Packages/EmpusaKit/Sources/EmpusaKit/Models/SwitchResource.swift b/Packages/EmpusaKit/Sources/EmpusaKit/Models/SwitchResource.swift index 0e04dce..8acf8f7 100644 --- a/Packages/EmpusaKit/Sources/EmpusaKit/Models/SwitchResource.swift +++ b/Packages/EmpusaKit/Sources/EmpusaKit/Models/SwitchResource.swift @@ -29,18 +29,25 @@ public enum SwitchResouceSource { public enum SwitchResource: String, CaseIterable { case hekate + case hekateIPL case atmosphere + case fusee case sigpatches case tinfoil case bootLogos case lockpickRCM + case hbAppStore public var displayName: String { switch self { case .hekate: "Hekate" + case .hekateIPL: + "hekate_ipl.ini" case .atmosphere: "Atmosphère" + case .fusee: + "Fusée" case .sigpatches: "Sigpatches" case .tinfoil: @@ -49,6 +56,8 @@ public enum SwitchResource: String, CaseIterable { "Boot logos" case .lockpickRCM: "Lockpick RCM" + case .hbAppStore: + "HB App Store" } } @@ -59,11 +68,21 @@ public enum SwitchResource: String, CaseIterable { .init(string: "https://api.github.com/repos/CTCaer/hekate/releases/latest")!, assetPrefix: "hekate_ctcaer_" ) + case .hekateIPL: + .link( + .init(string: "https://nh-server.github.io/switch-guide/files/emu/hekate_ipl.ini")!, + version: "(from NH Switch Guide)" + ) case .atmosphere: .github( .init(string: "https://api.github.com/repos/Atmosphere-NX/Atmosphere/releases/latest")!, assetPrefix: "atmosphere-" ) + case .fusee: + .github( + .init(string: "https://api.github.com/repos/Atmosphere-NX/Atmosphere/releases/latest")!, + assetPrefix: "fusee.bin" + ) case .sigpatches: .link( .init(string: "https://sigmapatches.coomer.party/sigpatches.zip")!, @@ -77,13 +96,18 @@ public enum SwitchResource: String, CaseIterable { case .bootLogos: .link( .init(string: "https://nh-server.github.io/switch-guide/files/bootlogos.zip")!, - version: nil + version: "(from NH Switch Guide)" ) case .lockpickRCM: .forgejo( .init(string: "https://vps.suchmeme.nl/git/api/v1/repos/mudkip/Lockpick_RCM/releases/latest")!, assetPrefix: "Lockpick_RCM.bin" ) + case .hbAppStore: + .github( + .init(string: "https://api.github.com/repos/fortheusers/hb-appstore/releases/latest")!, + assetPrefix: "appstore.nro" + ) } } @@ -91,8 +115,12 @@ public enum SwitchResource: String, CaseIterable { switch self { case .hekate: "hekate.zip" + case .hekateIPL: + "hekate_ipl.ini" case .atmosphere: "atmosphere.zip" + case .fusee: + "fusee.bin" case .sigpatches: "sigpatches.zip" case .tinfoil: @@ -101,49 +129,17 @@ public enum SwitchResource: String, CaseIterable { "bootlogos.zip" case .lockpickRCM: "Lockpick_RCM.bin" + case .hbAppStore: + "appstore.nro" } } var isAssetZipped: Bool { switch self { - case .lockpickRCM: + case .hekateIPL, .lockpickRCM, .hbAppStore, .fusee: false default: true } } } - -extension SwitchResource { - private var fileManager: FileManager { - .default - } - - func handleAsset( - at location: URL, - destination: URL, - progressSubject: CurrentValueSubject - ) throws { - switch self { - case .hekate, .bootLogos: - fileManager.merge( - atPath: location.appending(path: "bootloader").path(), - toPath: destination.appending(path: "bootloader").path(), - progressSubject: progressSubject - ) - - case .atmosphere, .sigpatches, .tinfoil: - fileManager.merge( - atPath: location.path(), - toPath: destination.path(), - progressSubject: progressSubject - ) - - case .lockpickRCM: - fileManager.moveFile( - at: location, - to: destination.appending(path: "bootloader").appending(path: "payloads") - ) - } - } -} diff --git a/Packages/EmpusaKit/Sources/EmpusaKit/Services/AssetService.swift b/Packages/EmpusaKit/Sources/EmpusaKit/Services/AssetService.swift index 7f3587b..3360a2e 100644 --- a/Packages/EmpusaKit/Sources/EmpusaKit/Services/AssetService.swift +++ b/Packages/EmpusaKit/Sources/EmpusaKit/Services/AssetService.swift @@ -8,7 +8,7 @@ enum AssetServiceError: Error { public protocol AssetServiceProtocol { func fetchRepositoryRelease(for resourceUrl: URL) async throws -> RepositoryRelease - func downloadAsset(for resource: SwitchResource, progressSubject: CurrentValueSubject) async throws -> Data + func downloadAsset(for resource: SwitchResource, progressSubject: CurrentValueSubject) async throws -> URL } public final class AssetService: AssetServiceProtocol { @@ -28,7 +28,7 @@ public final class AssetService: AssetServiceProtocol { public func downloadAsset( for resource: SwitchResource, progressSubject: CurrentValueSubject - ) async throws -> Data { + ) async throws -> URL { switch resource.source { case .github(let url, let assetPrefix), .forgejo(let url, let assetPrefix): let release = try await fetchRepositoryRelease(for: url) diff --git a/Packages/EmpusaKit/Sources/EmpusaKit/Services/StorageService.swift b/Packages/EmpusaKit/Sources/EmpusaKit/Services/StorageService.swift index eac46dd..7824938 100644 --- a/Packages/EmpusaKit/Sources/EmpusaKit/Services/StorageService.swift +++ b/Packages/EmpusaKit/Sources/EmpusaKit/Services/StorageService.swift @@ -5,10 +5,10 @@ import OSLog public protocol StorageServiceProtocol { func listExternalVolumes() throws -> [ExternalVolume] - func saveFile(data: Data, fileName: String) async throws -> URL func unzipFile(at location: URL, progressSubject: CurrentValueSubject) throws -> URL func unzipFile(at location: URL, to destination: URL, progressSubject: CurrentValueSubject) throws - func removeItem(at path: URL) + func moveItem(at location: URL, to destination: URL) throws + func removeItem(at location: URL) func zipDirectory(at location: URL, progressSubject: CurrentValueSubject) throws -> ZipFile } @@ -58,12 +58,6 @@ final public class StorageService: StorageServiceProtocol { } } - public func saveFile(data: Data, fileName: String) throws -> URL { - let destinationPath = tempDirectoryPath.appending(path: fileName) - try data.write(to: destinationPath) - return destinationPath - } - public func unzipFile( at location: URL, progressSubject: CurrentValueSubject @@ -99,9 +93,20 @@ final public class StorageService: StorageServiceProtocol { } } - public func removeItem(at path: URL) { + public func moveItem( + at location: URL, + to destination: URL + ) throws { + try? fileManager.removeItem(at: destination) + try fileManager.moveItem( + at: location, + to: destination + ) + } + + public func removeItem(at location: URL) { do { - try fileManager.removeItem(at: path) + try fileManager.removeItem(at: location) } catch { logger.error("StorageService: \(error.localizedDescription)") } @@ -135,3 +140,54 @@ final public class StorageService: StorageServiceProtocol { ) } } + +// MARK: - SwitchResource extensions + +extension SwitchResource { + private var fileManager: FileManager { + .default + } + + func handleAsset( + at location: URL, + destination: URL, + progressSubject: CurrentValueSubject + ) throws { + switch self { + case .hekate, .bootLogos: + fileManager.merge( + atPath: location.appending(path: "bootloader").path(), + toPath: destination.appending(path: "bootloader").path(), + progressSubject: progressSubject + ) + + case .atmosphere, .sigpatches, .tinfoil: + fileManager.merge( + atPath: location.path(), + toPath: destination.path(), + progressSubject: progressSubject + ) + + case .hekateIPL: + fileManager.moveFile( + at: location, + to: destination.appending(path: "bootloader"), + progressSubject: progressSubject + ) + + case .lockpickRCM, .fusee: + fileManager.moveFile( + at: location, + to: destination.appending(path: "bootloader").appending(path: "payloads"), + progressSubject: progressSubject + ) + + case .hbAppStore: + fileManager.moveFile( + at: location, + to: destination.appending(path: "switch"), + progressSubject: progressSubject + ) + } + } +}