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

Commit daa6e27

Browse files
authored
Refactor CertificateView and AddCertificateView
1 parent 4897b7a commit daa6e27

File tree

1 file changed

+42
-80
lines changed

1 file changed

+42
-80
lines changed

Sources/prostore/views/CertificateView.swift

Lines changed: 42 additions & 80 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
import SwiftUI
22
import UniformTypeIdentifiers
3-
43
// Centralized types to avoid conflicts
54
struct CertificateFileItem {
65
var name: String = ""
@@ -11,7 +10,6 @@ struct CustomCertificate: Identifiable {
1110
let displayName: String
1211
let folderName: String
1312
}
14-
1513
// MARK: - Date Extension for Formatting
1614
extension Date {
1715
func formattedWithOrdinal() -> String {
@@ -23,7 +21,6 @@ extension Date {
2321
let year = Calendar.current.component(.year, from: self)
2422
return "\(ordinal) of \(month) \(year)"
2523
}
26-
2724
private func ordinalSuffix(for number: Int) -> String {
2825
let suffix: String
2926
let ones = number % 10
@@ -42,7 +39,6 @@ extension Date {
4239
return "\(number)\(suffix)"
4340
}
4441
}
45-
4642
// MARK: - CertificateView (List + Add/Edit launchers)
4743
struct CertificateView: View {
4844
@State private var customCertificates: [CustomCertificate] = []
@@ -134,7 +130,6 @@ struct CertificateView: View {
134130
reloadCertificatesAndEnsureSelection()
135131
}
136132
}
137-
138133
private func certificateItem(for cert: CustomCertificate) -> some View {
139134
ZStack(alignment: .top) {
140135
certificateContent(for: cert)
@@ -153,7 +148,6 @@ struct CertificateView: View {
153148
certificateButtons(for: cert)
154149
}
155150
}
156-
157151
private func certificateContent(for cert: CustomCertificate) -> some View {
158152
VStack(alignment: .center, spacing: 12) {
159153
Text(cert.displayName)
@@ -163,7 +157,6 @@ struct CertificateView: View {
163157
.lineLimit(2)
164158
.minimumScaleFactor(0.8)
165159
.multilineTextAlignment(.center)
166-
167160
if let expiry = certExpiries[cert.folderName], let validExpiry = expiry {
168161
expiryDisplay(for: validExpiry)
169162
} else {
@@ -172,13 +165,11 @@ struct CertificateView: View {
172165
.fontWeight(.medium)
173166
.foregroundColor(.secondary)
174167
}
175-
176-
if let status = certStatuses[cert.folderName] {
177-
Text(status)
178-
.font(.caption)
179-
.fontWeight(.medium)
180-
.foregroundColor(statusColor(for: status))
181-
}
168+
let status = certStatuses[cert.folderName] ?? "Unknown"
169+
Text("Status: \(status)")
170+
.font(.caption)
171+
.fontWeight(.medium)
172+
.foregroundColor(status == "Revoked" ? .red : .secondary)
182173
}
183174
.padding(20)
184175
.frame(maxWidth: .infinity)
@@ -189,7 +180,6 @@ struct CertificateView: View {
189180
.stroke(selectedCert == cert.folderName ? Color.blue : Color.clear, lineWidth: 3)
190181
)
191182
}
192-
193183
private func expiryDisplay(for expiry: Date) -> some View {
194184
let now = Date()
195185
let components = Calendar.current.dateComponents([.day], from: now, to: expiry)
@@ -207,37 +197,26 @@ struct CertificateView: View {
207197
.fontWeight(.medium)
208198
.foregroundColor(.primary)
209199
}
210-
211200
private func certificateBackground(for cert: CustomCertificate) -> Color {
212-
guard let expiry = certExpiries[cert.folderName], let validExpiry = expiry,
213-
let status = certStatuses[cert.folderName] else {
201+
let status = certStatuses[cert.folderName] ?? "Unknown"
202+
if status == "Revoked" {
203+
return .red.opacity(0.15)
204+
}
205+
guard let expiry = certExpiries[cert.folderName], expiry != nil else {
214206
return Color.clear
215207
}
216208
let now = Date()
217-
let components = Calendar.current.dateComponents([.day], from: now, to: validExpiry)
209+
let components = Calendar.current.dateComponents([.day], from: now, to: expiry!)
218210
let days = components.day ?? 0
219-
if status != "Signed" || days <= 0 {
211+
switch days {
212+
case ..<0, 0:
220213
return .red.opacity(0.15)
221-
} else if days <= 30 {
214+
case 1...30:
222215
return .yellow.opacity(0.15)
223-
} else {
224-
return .green.opacity(0.15)
225-
}
226-
}
227-
228-
private func statusColor(for status: String) -> Color {
229-
switch status {
230-
case "Signed":
231-
return .green
232-
case "Revoked", "Mismatch":
233-
return .red
234-
case "Unknown":
235-
return .gray
236216
default:
237-
return .secondary
217+
return .green.opacity(0.15)
238218
}
239219
}
240-
241220
private func certificateButtons(for cert: CustomCertificate) -> some View {
242221
HStack {
243222
Button(action: {
@@ -251,9 +230,9 @@ struct CertificateView: View {
251230
.background(Color(.systemGray6).opacity(0.8))
252231
.clipShape(Circle())
253232
}
254-
233+
255234
Spacer()
256-
235+
257236
Button(action: {
258237
if customCertificates.count > 1 {
259238
certToDelete = cert
@@ -276,36 +255,23 @@ struct CertificateView: View {
276255
customCertificates = CertificateFileManager.shared.loadCertificates()
277256
selectedCert = UserDefaults.standard.string(forKey: "selectedCertificateFolder")
278257
ensureSelection()
279-
Task {
280-
await loadExpiries()
258+
loadExpiries()
259+
for cert in customCertificates {
260+
Task {
261+
let status = await CertRevokeChecker.checkRevocation(folderName: cert.folderName)
262+
await MainActor.run {
263+
certStatuses[cert.folderName] = status
264+
}
265+
}
281266
}
282267
}
283-
284-
private func loadExpiries() async {
285-
certExpiries = [:]
286-
certStatuses = [:]
268+
private func loadExpiries() {
287269
for cert in customCertificates {
288270
let folderName = cert.folderName
289271
let certDir = CertificateFileManager.shared.certificatesDirectory.appendingPathComponent(folderName)
290272
let provURL = certDir.appendingPathComponent("profile.mobileprovision")
291-
let p12URL = certDir.appendingPathComponent("certificate.p12")
292-
let passwordURL = certDir.appendingPathComponent("password.txt")
293-
let localExpiry = signer.getExpirationDate(provURL: provURL)
294-
let password = (try? String(contentsOf: passwordURL, encoding: .utf8)) ?? ""
295-
let result = await CertRevokeChecker.shared.check(p12URL: p12URL, provisionURL: provURL, password: password)
296-
switch result {
297-
case .success(let isSigned, let expires, let match):
298-
if match {
299-
certExpiries[folderName] = expires
300-
certStatuses[folderName] = isSigned ? "Signed" : "Revoked"
301-
} else {
302-
certStatuses[folderName] = "Mismatch"
303-
certExpiries[folderName] = localExpiry
304-
}
305-
case .failure, .networkError:
306-
certStatuses[folderName] = "Unknown"
307-
certExpiries[folderName] = localExpiry
308-
}
273+
let expiry = signer.getExpirationDate(provURL: provURL)
274+
certExpiries[folderName] = expiry
309275
}
310276
}
311277
private func ensureSelection() {
@@ -319,7 +285,7 @@ struct CertificateView: View {
319285
private func deleteCertificate(_ cert: CustomCertificate) {
320286
try? CertificateFileManager.shared.deleteCertificate(folderName: cert.folderName)
321287
customCertificates = CertificateFileManager.shared.loadCertificates()
322-
288+
323289
if selectedCert == cert.folderName {
324290
if let newSelection = customCertificates.first {
325291
selectedCert = newSelection.folderName
@@ -330,12 +296,9 @@ struct CertificateView: View {
330296
}
331297
}
332298
ensureSelection()
333-
Task {
334-
await loadExpiries()
335-
}
299+
loadExpiries()
336300
}
337301
}
338-
339302
// MARK: - Add / Edit View
340303
struct AddCertificateView: View {
341304
@Environment(\.dismiss) private var dismiss
@@ -371,7 +334,7 @@ struct AddCertificateView: View {
371334
}
372335
}
373336
.disabled(isChecking)
374-
337+
375338
Button(action: { activeSheet = .prov }) {
376339
HStack {
377340
Image(systemName: "gearshape.fill")
@@ -387,20 +350,20 @@ struct AddCertificateView: View {
387350
}
388351
.disabled(isChecking)
389352
}
390-
353+
391354
Section(header: Text("Display Name")) {
392355
TextField("Optional Display Name", text: $displayName)
393356
.disabled(isChecking)
394357
}
395-
358+
396359
Section(header: Text("Password")) {
397360
SecureField("Enter Password", text: $password)
398361
.disabled(isChecking)
399362
Text("Enter the password for the certificate. Leave it blank if there is no password needed.")
400363
.font(.caption)
401364
.foregroundColor(.secondary)
402365
}
403-
366+
404367
if !errorMessage.isEmpty {
405368
Text(errorMessage)
406369
.foregroundColor(.red)
@@ -453,24 +416,23 @@ struct AddCertificateView: View {
453416
let provURL = certFolder.appendingPathComponent("profile.mobileprovision")
454417
let passwordURL = certFolder.appendingPathComponent("password.txt")
455418
let nameURL = certFolder.appendingPathComponent("name.txt")
456-
419+
457420
p12File = CertificateFileItem(name: "certificate.p12", url: p12URL)
458421
provFile = CertificateFileItem(name: "profile.mobileprovision", url: provURL)
459-
422+
460423
if let pwData = try? Data(contentsOf: passwordURL), let pw = String(data: pwData, encoding: .utf8) {
461424
password = pw
462425
}
463426
if let nameData = try? Data(contentsOf: nameURL), let nameStr = String(data: nameData, encoding: .utf8) {
464427
displayName = nameStr
465428
}
466429
}
467-
468430
private func saveCertificate() {
469431
guard let p12URL = p12File?.url, let provURL = provFile?.url else { return }
470-
432+
471433
isChecking = true
472434
errorMessage = ""
473-
435+
474436
let workItem: DispatchWorkItem = DispatchWorkItem {
475437
do {
476438
var p12Data: Data
@@ -489,16 +451,16 @@ struct AddCertificateView: View {
489451
p12Data = try Data(contentsOf: p12URL)
490452
provData = try Data(contentsOf: provURL)
491453
}
492-
454+
493455
let checkResult = CertificatesManager.check(p12Data: p12Data, password: self.password, mobileProvisionData: provData)
494456
var dispatchError: String?
495-
457+
496458
switch checkResult {
497459
case .success(.success):
498460
if localDisplayName.isEmpty {
499461
localDisplayName = CertificatesManager.getCertificateName(mobileProvisionData: provData) ?? "Custom Certificate"
500462
}
501-
463+
502464
if let folder = self.editingCertificate?.folderName {
503465
try CertificateFileManager.shared.updateCertificate(folderName: folder, p12Data: p12Data, provData: provData, password: self.password, displayName: localDisplayName)
504466
} else {
@@ -512,7 +474,7 @@ struct AddCertificateView: View {
512474
case .failure(let error):
513475
dispatchError = "Error: \(error.localizedDescription)"
514476
}
515-
477+
516478
DispatchQueue.main.async {
517479
self.isChecking = false
518480
if let err = dispatchError {

0 commit comments

Comments
 (0)