1- // DownloadSignManager.swift
1+ // DownloadSignManager.swift - Updated version
22import Foundation
33import 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 add a certificate. " )
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: " Pairing file not found. Please follow setup to place pairing file in the 'ProStore' folder. " )
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,39 +282,65 @@ 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
299- let documents = fm. urls ( for: . documentDirectory, in: . userDomainMask) . first!
300- let appFolder = documents. appendingPathComponent ( " AppFolder " )
338+ let appFolder = fm. urls ( for: . documentDirectory, in: . userDomainMask) . first!
301339 if !fm. fileExists ( atPath: appFolder. path) {
302340 try ? fm. createDirectory ( at: appFolder, withIntermediateDirectories: true )
303341 }
304342 return appFolder
305343 }
306-
307344}
345+
346+
0 commit comments