Skip to content
This repository was archived by the owner on Mar 7, 2026. It is now read-only.

Commit c63c144

Browse files
authored
Enhance error handling in DownloadSignManager
Added error handling and user feedback for certificate selection and download process.
1 parent 28e32ee commit c63c144

1 file changed

Lines changed: 85 additions & 47 deletions

File tree

Sources/prostore/signing/DownloadSignManager.swift

Lines changed: 85 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// DownloadSignManager.swift
1+
// DownloadSignManager.swift - Updated version
22
import Foundation
33
import Combine
44

@@ -7,6 +7,8 @@ class DownloadSignManager: ObservableObject {
77
@Published var status: String = ""
88
@Published var isProcessing: Bool = false
99
@Published var showSuccess: Bool = false
10+
@Published var showError: Bool = false
11+
@Published var errorMessage: String = ""
1012

1113
private var downloadTask: URLSessionDownloadTask?
1214
private var downloadProgressObservation: NSKeyValueObservation?
@@ -20,13 +22,32 @@ class DownloadSignManager: ObservableObject {
2022
private let installPortion: Double = 1.0 - (0.33 + 0.33) // ~0.34
2123

2224
func downloadAndSign(app: AltApp) {
23-
guard let downloadURL = app.downloadURL else {
24-
self.status = "No download URL available"
25+
// Reset error state
26+
self.showError = false
27+
self.errorMessage = ""
28+
29+
// Validate certificate selection
30+
guard let selectedCertFolder = UserDefaults.standard.string(forKey: "selectedCertificateFolder") else {
31+
self.showError(message: "No certificate selected. Please select a certificate first.")
32+
return
33+
}
34+
35+
// Validate certificate files exist
36+
guard let certFiles = getCertificateFiles(for: selectedCertFolder) else {
37+
self.showError(message: "Certificate files not found or incomplete. Please check your certificate folder.")
38+
return
39+
}
40+
41+
// Check if pairing file exists (for installation)
42+
let fm = FileManager.default
43+
let pairingFile = getAppFolder().appendingPathComponent("pairingFile.plist")
44+
if !fm.fileExists(atPath: pairingFile.path) {
45+
self.showError(message: "Device not paired. Please complete device pairing first.")
2546
return
2647
}
2748

28-
guard let selectedCertFolder = UserDefaults.standard.string(forKey: "selectedCertificateFolder") else {
29-
self.status = "No certificate selected"
49+
guard let downloadURL = app.downloadURL else {
50+
self.showError(message: "No download URL available for this app")
3051
return
3152
}
3253

@@ -36,11 +57,17 @@ class DownloadSignManager: ObservableObject {
3657
self.showSuccess = false
3758

3859
DispatchQueue.global(qos: .userInitiated).async {
39-
self.performDownloadAndSign(downloadURL: downloadURL, appName: app.name, certFolder: selectedCertFolder)
60+
self.performDownloadAndSign(
61+
downloadURL: downloadURL,
62+
appName: app.name,
63+
p12URL: certFiles.p12URL,
64+
provURL: certFiles.provURL,
65+
password: certFiles.password
66+
)
4067
}
4168
}
4269

43-
private func performDownloadAndSign(downloadURL: URL, appName: String, certFolder: String) {
70+
private func performDownloadAndSign(downloadURL: URL, appName: String, p12URL: URL, provURL: URL, password: String) {
4471
// Step 1: Setup directories
4572
let fm = FileManager.default
4673
let appFolder = self.getAppFolder()
@@ -52,8 +79,7 @@ class DownloadSignManager: ObservableObject {
5279
}
5380
} catch {
5481
DispatchQueue.main.async {
55-
self.status = "Failed to create temp directory: \(error.localizedDescription)"
56-
self.isProcessing = false
82+
self.showError(message: "Failed to create temp directory: \(error.localizedDescription)")
5783
}
5884
return
5985
}
@@ -69,22 +95,12 @@ class DownloadSignManager: ObservableObject {
6995

7096
switch result {
7197
case .success:
72-
// Step 3: Get certificate files
73-
guard let (p12URL, provURL, password) = self.getCertificateFiles(for: certFolder) else {
74-
DispatchQueue.main.async {
75-
self.status = "Failed to get certificate files"
76-
self.isProcessing = false
77-
}
78-
return
79-
}
80-
81-
// Step 4: Sign the IPA
98+
// Step 3: Sign the IPA
8299
self.signIPA(ipaURL: tempIPAURL, p12URL: p12URL, provURL: provURL, password: password, appName: appName)
83100

84101
case .failure(let error):
85102
DispatchQueue.main.async {
86-
self.status = "Download failed: \(error.localizedDescription)"
87-
self.isProcessing = false
103+
self.showError(message: "Download failed: \(error.localizedDescription)")
88104
}
89105

90106
// Clean up temp file if it exists
@@ -108,9 +124,7 @@ class DownloadSignManager: ObservableObject {
108124

109125
if let error = error as NSError?, error.domain == NSURLErrorDomain, error.code == NSURLErrorCancelled {
110126
DispatchQueue.main.async {
111-
self.status = "Cancelled"
112-
self.isProcessing = false
113-
self.progress = 0.0
127+
self.showError(message: "Download cancelled")
114128
}
115129
completion(.failure(error))
116130
return
@@ -166,7 +180,7 @@ class DownloadSignManager: ObservableObject {
166180
task.resume()
167181
}
168182

169-
private func getCertificateFiles(for folderName: String) -> (p12URL: URL, provURL: URL, password: String)? {
183+
private func getCertificateFiles(for folderName: String) -> (p12URL: URL, provURL: URL, password: String)? {
170184
let fm = FileManager.default
171185
let certsDir = CertificateFileManager.shared.certificatesDirectory.appendingPathComponent(folderName)
172186

@@ -194,15 +208,15 @@ class DownloadSignManager: ObservableObject {
194208
p12URL: p12URL,
195209
provURL: provURL,
196210
p12Password: password,
197-
progressUpdate: { [weak self] status, progress in
198-
DispatchQueue.main.async {
199-
guard let self = self else { return }
200-
let overallProgress = self.downloadPortion + (progress * self.signPortion)
201-
self.progress = overallProgress
202-
let percentOfSign = Int(round(progress * 100))
203-
self.status = "\(status)"
204-
}
205-
},
211+
progressUpdate: { [weak self] status, progress in
212+
DispatchQueue.main.async {
213+
guard let self = self else { return }
214+
let overallProgress = self.downloadPortion + (progress * self.signPortion)
215+
self.progress = overallProgress
216+
let percentOfSign = Int(round(progress * 100))
217+
self.status = "\(status)"
218+
}
219+
},
206220
completion: { [weak self] result in
207221
DispatchQueue.main.async {
208222
guard let self = self else { return }
@@ -217,8 +231,7 @@ progressUpdate: { [weak self] status, progress in
217231
try? FileManager.default.removeItem(at: ipaURL)
218232

219233
case .failure(let error):
220-
self.status = "❌ Signing failed: \(error.localizedDescription)"
221-
self.isProcessing = false
234+
self.showError(message: "❌ Signing failed: \(error.localizedDescription)")
222235
try? FileManager.default.removeItem(at: ipaURL)
223236
}
224237
}
@@ -269,30 +282,56 @@ progressUpdate: { [weak self] status, progress in
269282

270283
} catch {
271284
await MainActor.run {
272-
self.status = "❌ Install failed: \(error.localizedDescription)"
273-
self.isProcessing = false
285+
self.showError(message: "❌ Install failed: \(error.localizedDescription)")
274286
self.installationStream = nil
275287
self.installationTask = nil
276288
}
277289
}
278290
}
279291
}
280292

281-
func cancel() {
282-
downloadTask?.cancel()
283-
installationTask?.cancel()
284-
installationStream = nil
285-
installationTask = nil
286-
287-
// Remove observer
288-
downloadProgressObservation = nil
293+
private func showError(message: String) {
294+
DispatchQueue.main.async {
295+
self.progress = 1.0 // Set to 100%
296+
self.status = message
297+
self.errorMessage = message
298+
self.showError = true
299+
self.isProcessing = true // Keep progress bar visible
300+
301+
// Hide progress bar after 5 seconds with red state
302+
DispatchQueue.main.asyncAfter(deadline: .now() + 5) {
303+
self.isProcessing = false
304+
self.showError = false
305+
self.progress = 0.0
306+
self.status = ""
307+
self.errorMessage = ""
308+
309+
// Clean up any tasks
310+
self.cancelTasks()
311+
}
312+
}
313+
}
289314

315+
func cancel() {
316+
cancelTasks()
317+
290318
DispatchQueue.main.async {
291319
self.isProcessing = false
320+
self.showSuccess = false
321+
self.showError = false
292322
self.status = "Cancelled"
293323
self.progress = 0.0
324+
self.errorMessage = ""
294325
}
295326
}
327+
328+
private func cancelTasks() {
329+
downloadTask?.cancel()
330+
installationTask?.cancel()
331+
installationStream = nil
332+
installationTask = nil
333+
downloadProgressObservation = nil
334+
}
296335

297336
private func getAppFolder() -> URL {
298337
let fm = FileManager.default
@@ -303,5 +342,4 @@ progressUpdate: { [weak self] status, progress in
303342
}
304343
return appFolder
305344
}
306-
307345
}

0 commit comments

Comments
 (0)