Skip to content

Commit

Permalink
feat: download checksum
Browse files Browse the repository at this point in the history
  • Loading branch information
TheMoonThatRises committed Jan 16, 2023
1 parent 496c15b commit bedfdc4
Show file tree
Hide file tree
Showing 27 changed files with 209 additions and 110 deletions.
4 changes: 4 additions & 0 deletions PlayCover.xcodeproj/project.pbxproj
Expand Up @@ -83,6 +83,7 @@
B6603E1328E2257800DEFA3F /* Uninstaller.swift in Sources */ = {isa = PBXBuildFile; fileRef = B6603E1228E2257800DEFA3F /* Uninstaller.swift */; };
B6825C3528F3D23600E3015A /* InstallSettings.swift in Sources */ = {isa = PBXBuildFile; fileRef = B6825C3428F3D23600E3015A /* InstallSettings.swift */; };
B6AB53C929232B4F00039B2E /* KeyCodeNames.swift in Sources */ = {isa = PBXBuildFile; fileRef = B6AB53C829232B4F00039B2E /* KeyCodeNames.swift */; };
B6ABDA2A2971EEF700A46E80 /* ProgressVM.swift in Sources */ = {isa = PBXBuildFile; fileRef = B6ABDA292971EEF700A46E80 /* ProgressVM.swift */; };
/* End PBXBuildFile section */

/* Begin PBXFileReference section */
Expand Down Expand Up @@ -186,6 +187,7 @@
B6603E1228E2257800DEFA3F /* Uninstaller.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Uninstaller.swift; sourceTree = "<group>"; };
B6825C3428F3D23600E3015A /* InstallSettings.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InstallSettings.swift; sourceTree = "<group>"; };
B6AB53C829232B4F00039B2E /* KeyCodeNames.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KeyCodeNames.swift; sourceTree = "<group>"; };
B6ABDA292971EEF700A46E80 /* ProgressVM.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProgressVM.swift; sourceTree = "<group>"; };
/* End PBXFileReference section */

/* Begin PBXFrameworksBuildPhase section */
Expand Down Expand Up @@ -305,6 +307,7 @@
6E66B0BE289DE6240099B907 /* StoreVM.swift */,
6E66B0D828A135360099B907 /* ToastVM.swift */,
53F4D29F26C43C690020167C /* Log.swift */,
B6ABDA292971EEF700A46E80 /* ProgressVM.swift */,
);
path = ViewModel;
sourceTree = "<group>";
Expand Down Expand Up @@ -672,6 +675,7 @@
53F4D29E26C43C040020167C /* UserIntentFlow.swift in Sources */,
6E66B0BF289DE6240099B907 /* StoreVM.swift in Sources */,
B6603E1328E2257800DEFA3F /* Uninstaller.swift in Sources */,
B6ABDA2A2971EEF700A46E80 /* ProgressVM.swift in Sources */,
AA71964B287A0EA800623C15 /* PlayRules.swift in Sources */,
6E66B0D928A135360099B907 /* ToastVM.swift in Sources */,
53F5E74326C1D37E005AED1D /* FileExtensions.swift in Sources */,
Expand Down
72 changes: 59 additions & 13 deletions PlayCover/AppInstaller/Downloader.swift
Expand Up @@ -6,6 +6,7 @@
//

import Foundation
import CryptoKit
import DownloadManager

/// DownloaderManager can be configured through this struct, default values are as the same as below
Expand Down Expand Up @@ -37,7 +38,7 @@ class DownloadApp {

func start() {
if !NetworkVM.isConnectedToNetwork() { return }
if installVM.installing {
if installVM.inProgress {
Log.shared.error(PlayCoverError.waitInstallation)
} else {
if let warningMessage = warning {
Expand All @@ -53,25 +54,61 @@ class DownloadApp {

if alert.runModal() == .alertSecondButtonReturn {
return
} else {
proceedDownload()
}
} else {
proceedDownload()
}

proceedDownload()
}
}

func cancel() {
downloader.cancelAllDownloads()
downloadVM.downloading = false
downloadVM.progress = 0

downloadVM.next(.canceled, 0.95, 1.0)
downloadVM.storeAppData = nil
}

private func checksumAlert(originalSum: String, givenSum: String, completion: @escaping(Bool) -> Void) {
Task { @MainActor in
let alert = NSAlert()
alert.messageText = NSLocalizedString("playapp.download.differentChecksum", comment: "")
alert.informativeText = String(
format: NSLocalizedString("playapp.download.differentChecksumDesc", comment: ""),
arguments: [originalSum, givenSum]
)
alert.alertStyle = .warning
alert.addButton(withTitle: NSLocalizedString("button.Proceed", comment: ""))
alert.addButton(withTitle: NSLocalizedString("button.Cancel", comment: ""))

completion(alert.runModal() == .alertFirstButtonReturn)
}
}

private func verifyChecksum(checksum: String?, file: URL?, completion: @escaping(Bool) -> Void) {
Task {
if let originalSum = self.downloadVM.storeAppData?.checksum, !originalSum.isEmpty, let fileURL = file {
do {
let sha256 = SHA256.hash(data: try Data(contentsOf: fileURL))
.map { String(format: "%02hhx", $0) }
.joined()

if originalSum != sha256 {
checksumAlert(originalSum: originalSum, givenSum: sha256, completion: completion)
return
}
} catch {
print("Error in calculating sha256sum: \(error)")
}
}

completion(true)
}
}

private func proceedDownload() {
self.downloadVM.storeAppData = self.app
self.downloadVM.downloading = true
self.downloadVM.next(.downloading, 0.0, 0.7)

var tmpDir: URL?
do {
tmpDir = try FileManager.default.url(for: .itemReplacementDirectory,
Expand All @@ -84,17 +121,26 @@ class DownloadApp {
// progress is a Float
self.downloadVM.progress = Double(progress)
}, onCompletion: { error, fileURL in
self.downloadVM.next(.integrity, 0.7, 0.95)

guard error == nil else {
self.downloadVM.downloading = false
self.downloadVM.progress = 0
self.downloadVM.next(.failed, 0.95, 1.0)
self.downloadVM.storeAppData = nil
return Log.shared.error(error!)
}
self.downloadVM.downloading = false
self.downloadVM.progress = 0
self.proceedInstall(fileURL)

self.verifyChecksum(checksum: self.downloadVM.storeAppData?.checksum, file: fileURL) { completing in
self.downloadVM.next(completing ? .finish : .failed, 0.95, 1.0)
if completing {
Task { @MainActor in
self.proceedInstall(fileURL)
}
}
}
})
} catch {
self.downloadVM.next(.failed, 0.95, 1.0)

if let tmpDir = tmpDir {
FileManager.default.delete(at: tmpDir)
}
Expand Down
17 changes: 14 additions & 3 deletions PlayCover/ViewModel/DownloadVM.swift
Expand Up @@ -7,10 +7,21 @@

import Foundation

class DownloadVM: ObservableObject {
@Published var progress: Double = 0
@Published var downloading = false
enum DownloadStepsNative: String {
case downloading = "playapp.download.downloading",
integrity = "playapp.download.integrityCheck",
finish = "playapp.progress.finished",
failed = "playapp.progress.failed",
canceled = "playapp.progress.canceled"
}

class DownloadVM: ProgressVM<DownloadStepsNative> {
@Published var storeAppData: StoreAppData?

static let shared = DownloadVM()

init() {
super.init(start: .downloading, ends: [.finish, .failed, .canceled])
}

}
31 changes: 6 additions & 25 deletions PlayCover/ViewModel/InstallVM.swift
Expand Up @@ -12,35 +12,16 @@ enum InstallStepsNative: String {
sign = "playapp.install.signing",
library = "playapp.install.addToLib",
begin = "playapp.install.copy",
finish = "playapp.install.finished",
failed = "playapp.install.failed"
finish = "playapp.progress.finished",
failed = "playapp.progress.failed"
}

class InstallVM: ObservableObject {

@Published var status: String = NSLocalizedString(InstallStepsNative.begin.rawValue, comment: "")
@Published var progress = 0.0
@Published var installing = false
class InstallVM: ProgressVM<InstallStepsNative> {

static let shared = InstallVM()

func next(_ step: InstallStepsNative, _ startProgress: Double, _ stopProgress: Double) {
Task { @MainActor in
self.progress = startProgress
self.status = NSLocalizedString(step.rawValue, comment: "")
if step == .begin {
self.installing = true
} else if step == .finish || step == .failed {
self.progress = 1.0
try await Task.sleep(nanoseconds: 1500000000)
self.installing = false
}
while self.status == NSLocalizedString(step.rawValue, comment: "") {
try await Task.sleep(nanoseconds: 50000000)
if self.progress < stopProgress {
self.progress += 0.002
}
}
}
init() {
super.init(start: .begin, ends: [.finish, .failed])
}

}
48 changes: 48 additions & 0 deletions PlayCover/ViewModel/ProgressVM.swift
@@ -0,0 +1,48 @@
//
// ProgressVM.swift
// PlayCover
//
// Created by TheMoonThatRises on 1/13/23.
//

import Foundation

class ProgressVM<Steps: RawRepresentable & Equatable>: ObservableObject where Steps.RawValue == String {
@Published var progress = 0.0
@Published var inProgress = false
@Published var status: Steps

private let starting: Steps
private let ends: [Steps]

/// - Parameters:
/// - starts: The initial starting value
/// - ends: Ending conditions
init(start: Steps, ends: [Steps]) {
self.status = start
self.starting = start
self.ends = ends
}

func next(_ step: Steps, _ startProgress: Double, _ stopProgress: Double) {
Task { @MainActor in
self.progress = startProgress
self.status = step

if step == self.starting {
self.inProgress = true
} else if self.ends.contains(step) {
self.progress = 1.0
try await Task.sleep(nanoseconds: 1500000000)
self.inProgress = false
} else {
while self.status == step {
try await Task.sleep(nanoseconds: 50000000)
if self.progress < stopProgress {
self.progress += 0.002
}
}
}
}
}
}
1 change: 1 addition & 0 deletions PlayCover/ViewModel/StoreVM.swift
Expand Up @@ -188,4 +188,5 @@ struct StoreAppData: Codable, Equatable {
let version: String
let itunesLookup: String
let link: String
let checksum: String?
}
6 changes: 3 additions & 3 deletions PlayCover/Views/App Views/StoreAppView.swift
Expand Up @@ -34,7 +34,7 @@ struct StoreAppView: View {
warningMessage: $warningMessage)
.gesture(TapGesture(count: 2).onEnded {
if let url = URL(string: app.link) {
if downloadVM.downloading {
if downloadVM.inProgress {
Log.shared.error(PlayCoverError.waitDownload)
} else {
DownloadApp(url: url, app: app,
Expand Down Expand Up @@ -114,7 +114,7 @@ struct StoreAppConditionalView: View {
.shadow(radius: 1)
.padding(.horizontal, 15)
.padding(.vertical, 5)
if downloadVM.downloading && downloadVM.storeAppData == app {
if downloadVM.inProgress && downloadVM.storeAppData == app {
ProgressView(value: downloadVM.progress)
.progressViewStyle(.circular)
}
Expand Down Expand Up @@ -154,7 +154,7 @@ struct StoreAppConditionalView: View {
.frame(width: 60, height: 60)
.cornerRadius(15)
.shadow(radius: 1)
if downloadVM.downloading && downloadVM.storeAppData == app {
if downloadVM.inProgress && downloadVM.storeAppData == app {
VStack {
Spacer()
ProgressView(value: downloadVM.progress)
Expand Down
4 changes: 2 additions & 2 deletions PlayCover/Views/MenuBarView.swift
Expand Up @@ -60,9 +60,9 @@ struct PlayCoverViewMenuView: Commands {
CommandGroup(replacing: .importExport) {
Button("menubar.exportToSideloady") {
Task {
if InstallVM.shared.installing {
if InstallVM.shared.inProgress {
Log.shared.error(PlayCoverError.waitInstallation)
} else if DownloadVM.shared.downloading {
} else if DownloadVM.shared.inProgress {
Log.shared.error(PlayCoverError.waitDownload)
} else {
await NSOpenPanel.selectIPA { result in
Expand Down
1 change: 1 addition & 0 deletions PlayCover/Views/PlayCoverApp.swift
Expand Up @@ -72,6 +72,7 @@ struct PlayCoverApp: App {
MainView(xcodeCliInstalled: $xcodeCliInstalled,
isSigningSetupShown: $isSigningSetupShown)
.environmentObject(InstallVM.shared)
.environmentObject(DownloadVM.shared)
.environmentObject(AppsVM.shared)
.environmentObject(storeVM)
.environmentObject(AppIntegrity())
Expand Down
9 changes: 5 additions & 4 deletions PlayCover/Views/Sidebar Views/AppLibraryView.swift
Expand Up @@ -8,6 +8,7 @@ import SwiftUI
struct AppLibraryView: View {
@EnvironmentObject var appsVM: AppsVM
@EnvironmentObject var installVM: InstallVM
@EnvironmentObject var downloadVM: DownloadVM

@Binding var selectedBackgroundColor: Color
@Binding var selectedTextColor: Color
Expand Down Expand Up @@ -65,9 +66,9 @@ struct AppLibraryView: View {
}
ToolbarItem(placement: .primaryAction) {
Button(action: {
if installVM.installing {
if installVM.inProgress {
Log.shared.error(PlayCoverError.waitInstallation)
} else if DownloadVM.shared.downloading {
} else if downloadVM.inProgress {
Log.shared.error(PlayCoverError.waitDownload)
} else {
selectFile()
Expand Down Expand Up @@ -101,10 +102,10 @@ struct AppLibraryView: View {
showLegacyConvertAlert = LegacySettings.doesMonolithExist
}
.onDrop(of: ["public.url", "public.file-url"], isTargeted: nil) { (items) -> Bool in
if installVM.installing {
if installVM.inProgress {
Log.shared.error(PlayCoverError.waitInstallation)
return false
} else if DownloadVM.shared.downloading {
} else if downloadVM.inProgress {
Log.shared.error(PlayCoverError.waitDownload)
return false
} else if let item = items.first {
Expand Down

0 comments on commit bedfdc4

Please sign in to comment.