@@ -7,60 +7,37 @@ class DownloadSignManager: ObservableObject {
77 @Published var status : String = " "
88 @Published var isProcessing : Bool = false
99 @Published var showSuccess : Bool = false
10-
10+
1111 private var downloadTask : URLSessionDownloadTask ?
1212 private var cancellables = Set < AnyCancellable > ( )
13- private var hideTimer : Timer ?
14-
13+
1514 func downloadAndSign( app: AltApp ) {
1615 guard let downloadURL = app. downloadURL else {
17- self . showTemporaryError ( " No download URL available " )
16+ self . status = " No download URL available "
1817 return
1918 }
20-
19+
2120 guard let selectedCertFolder = UserDefaults . standard. string ( forKey: " selectedCertificateFolder " ) else {
22- self . showTemporaryError ( " Please add and select a certificate first! " )
21+ self . status = " No certificate selected "
2322 return
2423 }
25-
24+
2625 self . isProcessing = true
2726 self . progress = 0.0
2827 self . status = " Starting download... "
2928 self . showSuccess = false
30-
29+
3130 DispatchQueue . global ( qos: . userInitiated) . async {
3231 self . performDownloadAndSign ( downloadURL: downloadURL, appName: app. name, certFolder: selectedCertFolder)
3332 }
3433 }
3534
36- private func showTemporaryError( _ message: String ) {
37- DispatchQueue . main. async {
38- self . isProcessing = true
39- self . progress = 0.0
40- self . status = message
41- self . showSuccess = false
42-
43- // Cancel any existing timer
44- self . hideTimer? . invalidate ( )
45-
46- // Hide the bar after 5 seconds
47- self . hideTimer = Timer . scheduledTimer ( withTimeInterval: 5.0 , repeats: false ) { _ in
48- DispatchQueue . main. async {
49- self . isProcessing = false
50- self . progress = 0.0
51- self . status = " "
52- self . showSuccess = false
53- }
54- }
55- }
56- }
57-
5835 private func performDownloadAndSign( downloadURL: URL , appName: String , certFolder: String ) {
5936 // Step 1: Setup directories
6037 let fm = FileManager . default
6138 let appFolder = self . getAppFolder ( )
6239 let tempDir = appFolder. appendingPathComponent ( " temp " )
63-
40+
6441 do {
6542 if !fm. fileExists ( atPath: tempDir. path) {
6643 try fm. createDirectory ( at: tempDir, withIntermediateDirectories: true )
@@ -72,13 +49,13 @@ class DownloadSignManager: ObservableObject {
7249 }
7350 return
7451 }
75-
52+
7653 let tempIPAURL = tempDir. appendingPathComponent ( " \( UUID ( ) . uuidString) .ipa " )
77-
54+
7855 // Step 2: Download the IPA
7956 self . downloadIPA ( from: downloadURL, to: tempIPAURL) { [ weak self] result in
8057 guard let self = self else { return }
81-
58+
8259 switch result {
8360 case . success:
8461 // Step 3: Get certificate files
@@ -89,38 +66,38 @@ class DownloadSignManager: ObservableObject {
8966 }
9067 return
9168 }
92-
69+
9370 // Step 4: Sign the IPA
9471 self . signIPA ( ipaURL: tempIPAURL, p12URL: p12URL, provURL: provURL, password: password, appName: appName)
95-
72+
9673 case . failure( let error) :
9774 DispatchQueue . main. async {
9875 self . status = " Download failed: \( error. localizedDescription) "
9976 self . isProcessing = false
10077 }
101-
78+
10279 // Clean up temp file if it exists
10380 try ? fm. removeItem ( at: tempIPAURL)
10481 }
10582 }
10683 }
107-
84+
10885 private func downloadIPA( from url: URL , to destination: URL , completion: @escaping ( Result < Void , Error > ) -> Void ) {
10986 let semaphore = DispatchSemaphore ( value: 0 )
110-
87+
11188 let task = URLSession . shared. downloadTask ( with: url) { tempURL, response, error in
11289 defer { semaphore. signal ( ) }
113-
90+
11491 if let error = error {
11592 completion ( . failure( error) )
11693 return
11794 }
118-
95+
11996 guard let tempURL = tempURL else {
12097 completion ( . failure( NSError ( domain: " Download " , code: - 1 , userInfo: [ NSLocalizedDescriptionKey: " No temp URL returned " ] ) ) )
12198 return
12299 }
123-
100+
124101 do {
125102 let fm = FileManager . default
126103 if fm. fileExists ( atPath: destination. path) {
@@ -132,7 +109,7 @@ class DownloadSignManager: ObservableObject {
132109 completion ( . failure( error) )
133110 }
134111 }
135-
112+
136113 // Observe download progress
137114 var observation : NSKeyValueObservation ?
138115 observation = task. progress. observe ( \. fractionCompleted) { [ weak self] progress, _ in
@@ -143,104 +120,103 @@ class DownloadSignManager: ObservableObject {
143120 self ? . status = " Downloading... ( \( percent) %) "
144121 }
145122 }
146-
123+
147124 self . downloadTask = task
148125 task. resume ( )
149-
126+
150127 // Wait for download to complete
151128 DispatchQueue . global ( qos: . userInitiated) . async {
152129 semaphore. wait ( )
153130 observation? . invalidate ( )
154131 }
155132 }
156-
157- private func getCertificateFiles( for folderName: String ) -> ( p12URL: URL , provURL: URL , password: String ) ? {
133+
134+ private func getCertificateFiles( for folderName: String ) -> ( p12URL: URL , provURL: URL , password: String ) ? {
158135 let fm = FileManager . default
159136 let certsDir = CertificateFileManager . shared. certificatesDirectory. appendingPathComponent ( folderName)
160-
137+
161138 let p12URL = certsDir. appendingPathComponent ( " certificate.p12 " )
162139 let provURL = certsDir. appendingPathComponent ( " profile.mobileprovision " )
163140 let passwordURL = certsDir. appendingPathComponent ( " password.txt " )
164-
141+
165142 guard fm. fileExists ( atPath: p12URL. path) ,
166143 fm. fileExists ( atPath: provURL. path) ,
167144 fm. fileExists ( atPath: passwordURL. path) else {
168145 return nil
169146 }
170-
147+
171148 do {
172149 let password = try String ( contentsOf: passwordURL, encoding: . utf8) . trimmingCharacters ( in: . whitespacesAndNewlines)
173150 return ( p12URL, provURL, password)
174151 } catch {
175152 return nil
176153 }
177154 }
178-
179- private func signIPA( ipaURL: URL , p12URL: URL , provURL: URL , password: String , appName: String ) {
180- DispatchQueue . main. async {
181- self . status = " Starting signing process... "
182- self . progress = 0.5
183- }
184-
185- signer. sign (
186- ipaURL: ipaURL,
187- p12URL: p12URL,
188- provURL: provURL,
189- p12Password: password,
190- progressUpdate: { [ weak self] status, progress in
191- DispatchQueue . main. async {
192- let overallProgress = 0.5 + ( progress * 0.5 )
193- self ? . progress = overallProgress
194- let percent = Int ( overallProgress * 100 )
195- self ? . status = " \( status) ( \( percent) %) "
196- }
197- } ,
198- completion: { [ weak self] result in
199- DispatchQueue . main. async {
200- switch result {
201- case . success( let signedIPAURL) :
202- self ? . progress = 1.0
203- self ? . status = " ✅ Successfully signed ipa! Installing app now... "
204- self ? . showSuccess = true
205-
206- Task {
207- do {
208- try await installApp ( from: signedIPAURL)
209- } catch {
210- self ? . status = " ❌ Install failed: \( error. localizedDescription) "
211- }
212- }
213- DispatchQueue . main. asyncAfter ( deadline: . now( ) + 3 ) {
214- self ? . isProcessing = false
215- self ? . showSuccess = false
216- self ? . progress = 0.0
217- self ? . status = " "
155+
156+ private func signIPA( ipaURL: URL , p12URL: URL , provURL: URL , password: String , appName: String ) {
157+ DispatchQueue . main. async {
158+ self . status = " Starting signing process... "
159+ self . progress = 0.5
160+ }
161+
162+ signer. sign (
163+ ipaURL: ipaURL,
164+ p12URL: p12URL,
165+ provURL: provURL,
166+ p12Password: password,
167+ progressUpdate: { [ weak self] status, progress in
168+ DispatchQueue . main. async {
169+ let overallProgress = 0.5 + ( progress * 0.5 )
170+ self ? . progress = overallProgress
171+ let percent = Int ( overallProgress * 100 )
172+ self ? . status = " \( status) ( \( percent) %) "
173+ }
174+ } ,
175+ completion: { [ weak self] result in
176+ DispatchQueue . main. async {
177+ switch result {
178+ case . success( let signedIPAURL) :
179+ self ? . progress = 1.0
180+ self ? . status = " ✅ Successfully signed ipa! Installing app now... "
181+ self ? . showSuccess = true
182+
183+ Task {
184+ do {
185+ try await installApp ( from: signedIPAURL)
186+ } catch {
187+ self ? . status = " ❌ Install failed: \( error. localizedDescription) "
218188 }
219-
220- // Clean up original downloaded IPA
221- try ? FileManager . default. removeItem ( at: ipaURL)
222-
223- case . failure( let error) :
224- self ? . status = " ❌ Signing failed: \( error. localizedDescription) "
189+ }
190+
191+ DispatchQueue . main. asyncAfter ( deadline: . now( ) + 3 ) {
225192 self ? . isProcessing = false
226- try ? FileManager . default. removeItem ( at: ipaURL)
193+ self ? . showSuccess = false
194+ self ? . progress = 0.0
195+ self ? . status = " "
227196 }
197+
198+ // Clean up original downloaded IPA
199+ try ? FileManager . default. removeItem ( at: ipaURL)
200+
201+ case . failure( let error) :
202+ self ? . status = " ❌ Signing failed: \( error. localizedDescription) "
203+ self ? . isProcessing = false
204+ try ? FileManager . default. removeItem ( at: ipaURL)
228205 }
229206 }
230- )
231- }
232-
207+ }
208+ )
209+ }
210+
233211 func cancel( ) {
234212 downloadTask? . cancel ( )
235- hideTimer? . invalidate ( )
236213 DispatchQueue . main. async {
237214 self . isProcessing = false
238215 self . status = " Cancelled "
239216 self . progress = 0.0
240- self . showSuccess = false
241217 }
242218 }
243-
219+
244220 private func getAppFolder( ) -> URL {
245221 let fm = FileManager . default
246222 let documents = fm. urls ( for: . documentDirectory, in: . userDomainMask) . first!
@@ -250,4 +226,5 @@ class DownloadSignManager: ObservableObject {
250226 }
251227 return appFolder
252228 }
229+
253230}
0 commit comments