@@ -5,7 +5,11 @@ import ProStoreTools
55struct SignerView : View {
66 @StateObject private var ipa = FileItem ( )
77 @State private var isProcessing = false
8- @State private var progressMessage = " "
8+ @State private var overallProgress : Double = 0.0
9+ @State private var currentStage : String = " "
10+ @State private var barColor : Color = . blue
11+ @State private var isError : Bool = false
12+ @State private var errorDetails : String = " "
913 @State private var showActivity = false
1014 @State private var activityURL : URL ? = nil
1115 @State private var showPickerFor : PickerKind ? = nil
@@ -73,18 +77,26 @@ struct SignerView: View {
7377 }
7478 . padding ( . vertical, 8 )
7579
76- Section ( header: Text ( " Status " )
77- . font ( . headline)
78- . foregroundColor ( . primary)
79- . padding ( . top, 8 ) ) {
80- HStack {
81- if isProcessing {
82- ProgressView ( )
83- . padding ( . trailing, 8 )
80+ if isProcessing || !currentStage. isEmpty {
81+ Section ( header: Text ( " Progress " )
82+ . font ( . headline)
83+ . foregroundColor ( . primary)
84+ . padding ( . top, 8 ) ) {
85+ HStack {
86+ Text ( currentStage)
87+ . foregroundColor ( currentStage == " Error " ? . red : currentStage == " Done! " ? . green : . primary)
88+ ProgressView ( value: overallProgress)
89+ . progressViewStyle ( . linear)
90+ . tint ( barColor)
91+ . frame ( maxWidth: . infinity)
92+ Text ( " \( Int ( overallProgress * 100 ) ) % " )
93+ . foregroundColor ( currentStage == " Error " ? . red : currentStage == " Done! " ? . green : . primary)
94+ }
95+ if isError {
96+ Text ( errorDetails)
97+ . foregroundColor ( . red)
98+ . font ( . subheadline)
8499 }
85- Text ( progressMessage)
86- . foregroundColor ( progressMessage. contains ( " Error " ) ? . red : progressMessage. contains ( " Done " ) ? . green : . primary)
87- . animation ( . easeIn, value: progressMessage)
88100 }
89101 }
90102 }
@@ -135,11 +147,23 @@ struct SignerView: View {
135147
136148 func runSign( ) {
137149 guard let ipaURL = ipa. url else {
138- progressMessage = " Pick IPA file first 😅 "
150+ currentStage = " Error "
151+ errorDetails = " Pick IPA file first 😅 "
152+ isError = true
153+ withAnimation {
154+ overallProgress = 1.0
155+ barColor = . red
156+ }
139157 return
140158 }
141159 guard let selectedFolder = UserDefaults . standard. string ( forKey: " selectedCertificateFolder " ) else {
142- progressMessage = " No certificate selected 😅 "
160+ currentStage = " Error "
161+ errorDetails = " No certificate selected 😅 "
162+ isError = true
163+ withAnimation {
164+ overallProgress = 1.0
165+ barColor = . red
166+ }
143167 return
144168 }
145169 let certDir = CertificateFileManager . shared. certificatesDirectory. appendingPathComponent ( selectedFolder)
@@ -148,7 +172,13 @@ struct SignerView: View {
148172 let passwordURL = certDir. appendingPathComponent ( " password.txt " )
149173
150174 guard FileManager . default. fileExists ( atPath: p12URL. path) , FileManager . default. fileExists ( atPath: provURL. path) else {
151- progressMessage = " Error loading certificate files 😅 "
175+ currentStage = " Error "
176+ errorDetails = " Error loading certificate files 😅 "
177+ isError = true
178+ withAnimation {
179+ overallProgress = 1.0
180+ barColor = . red
181+ }
152182 return
153183 }
154184
@@ -161,7 +191,11 @@ struct SignerView: View {
161191 }
162192
163193 isProcessing = true
164- progressMessage = " Starting signing process... "
194+ currentStage = " Preparing "
195+ overallProgress = 0.0
196+ barColor = . blue
197+ isError = false
198+ errorDetails = " "
165199
166200 ProStoreTools . sign (
167201 ipaURL: ipaURL,
@@ -170,7 +204,7 @@ struct SignerView: View {
170204 p12Password: p12Password,
171205 progressUpdate: { message in
172206 DispatchQueue . main. async {
173- progressMessage = message
207+ updateProgress ( from : message)
174208 }
175209 } ,
176210 completion: { result in
@@ -179,13 +213,62 @@ struct SignerView: View {
179213 switch result {
180214 case . success( let signedIPAURL) :
181215 activityURL = signedIPAURL
182- showActivity = true
183- progressMessage = " Done! ✅ IPA ready to share 🎉 "
216+ withAnimation {
217+ overallProgress = 1.0
218+ barColor = . green
219+ currentStage = " Done! "
220+ }
221+ DispatchQueue . main. asyncAfter ( deadline: . now( ) + 1 ) {
222+ showActivity = true
223+ }
184224 case . failure( let error) :
185- progressMessage = " Error ❌: \( error. localizedDescription) "
225+ withAnimation {
226+ overallProgress = 1.0
227+ barColor = . red
228+ currentStage = " Error "
229+ }
230+ isError = true
231+ errorDetails = error. localizedDescription
186232 }
187233 }
188234 }
189235 )
190236 }
237+
238+ private func updateProgress( from message: String ) {
239+ withAnimation {
240+ if message. contains ( " Preparing " ) {
241+ currentStage = " Preparing "
242+ overallProgress = 0.0
243+ } else if message. contains ( " Unzipping " ) {
244+ currentStage = " Unzipping "
245+ if let pct = extractPercentage ( from: message) {
246+ overallProgress = 0.25 + ( pct / 100.0 ) * 0.25
247+ } else {
248+ overallProgress = 0.25
249+ }
250+ } else if message. contains ( " Signing " ) {
251+ currentStage = " Signing "
252+ overallProgress = 0.5
253+ } else if message. contains ( " Zipping " ) {
254+ currentStage = " Zipping "
255+ if let pct = extractPercentage ( from: message) {
256+ overallProgress = 0.75 + ( pct / 100.0 ) * 0.25
257+ } else {
258+ overallProgress = 0.75
259+ }
260+ }
261+ }
262+ }
263+
264+ private func extractPercentage( from message: String ) -> Double ? {
265+ if let range = message. range ( of: " ( " ) {
266+ let substring = message [ range. lowerBound... ]
267+ if let endRange = substring. range ( of: " %) " ) {
268+ let pctString = substring [ ..< endRange. lowerBound] . dropFirst ( )
269+ return Double ( pctString)
270+ }
271+ }
272+ return nil
273+ }
191274}
0 commit comments