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

Commit c08ae27

Browse files
authored
Attempt to fix bug
1 parent 3185b54 commit c08ae27

File tree

1 file changed

+73
-80
lines changed

1 file changed

+73
-80
lines changed

Sources/prostore/views/CertificateView.swift

Lines changed: 73 additions & 80 deletions
Original file line numberDiff line numberDiff line change
@@ -32,28 +32,25 @@ class CertificateFileManager {
3232
}
3333

3434
func loadCertificates() -> [CustomCertificate] {
35-
guard let subdirectories = try? fileManager.contentsOfDirectory(at: certificatesDirectory, includingPropertiesForKeys: [.isDirectoryKey], options: [.skipsHiddenFiles]) else {
35+
var resultCerts: [CustomCertificate] = []
36+
guard let folders = try? fileManager.contentsOfDirectory(at: certificatesDirectory, includingPropertiesForKeys: nil, options: [.skipsHiddenFiles]) else {
3637
return []
3738
}
3839

39-
var certificates: [CustomCertificate] = []
40-
for folder in subdirectories {
41-
var isDir: ObjCBool = false
42-
if fileManager.fileExists(atPath: folder.path, isDirectory: &isDir), !isDir.boolValue { continue }
43-
40+
for folder in folders {
4441
let nameURL = folder.appendingPathComponent("name.txt")
4542
if fileManager.fileExists(atPath: nameURL.path) {
46-
do {
47-
let nameData = try Data(contentsOf: nameURL)
48-
if let displayName = String(data: nameData, encoding: .utf8) {
49-
certificates.append(CustomCertificate(displayName: displayName, folderName: folder.lastPathComponent))
50-
}
51-
} catch {
52-
print("Error loading name: \(error)")
43+
if let nameData = try? Data(contentsOf: nameURL),
44+
let nameString = String(data: nameData, encoding: .utf8) {
45+
resultCerts.append(CustomCertificate(displayName: nameString, folderName: folder.lastPathComponent))
5346
}
47+
} else {
48+
// Fallback display name if missing
49+
resultCerts.append(CustomCertificate(displayName: folder.lastPathComponent, folderName: folder.lastPathComponent))
5450
}
5551
}
56-
return certificates.sorted { $0.displayName < $1.displayName }
52+
53+
return resultCerts
5754
}
5855

5956
func saveCertificate(p12Data: Data, provData: Data, password: String, displayName: String) throws -> String {
@@ -68,7 +65,6 @@ class CertificateFileManager {
6865
let p12URL = folder.appendingPathComponent("certificate.p12")
6966
let provURL = folder.appendingPathComponent("profile.mobileprovision")
7067
let passwordURL = folder.appendingPathComponent("password.txt")
71-
7268
if fileManager.fileExists(atPath: p12URL.path) && fileManager.fileExists(atPath: provURL.path) && fileManager.fileExists(atPath: passwordURL.path) {
7369
do {
7470
let existingP12Data = try Data(contentsOf: p12URL)
@@ -90,44 +86,37 @@ class CertificateFileManager {
9086
}
9187
}
9288

93-
// Find unique folder name
94-
var candidate = baseName
95-
var count = 1
96-
while fileManager.fileExists(atPath: certificatesDirectory.appendingPathComponent(candidate).path) {
97-
candidate = "\(baseName) (\(count))"
98-
count += 1
89+
// Create folder
90+
var finalName = baseName
91+
var counter = 1
92+
var folderURL = certificatesDirectory.appendingPathComponent(finalName)
93+
while fileManager.fileExists(atPath: folderURL.path) {
94+
counter += 1
95+
finalName = "\(baseName)-\(counter)"
96+
folderURL = certificatesDirectory.appendingPathComponent(finalName)
9997
}
98+
try fileManager.createDirectory(at: folderURL, withIntermediateDirectories: true)
10099

101-
let certificateFolder = certificatesDirectory.appendingPathComponent(candidate)
102-
try fileManager.createDirectory(at: certificateFolder, withIntermediateDirectories: true)
103-
104-
try p12Data.write(to: certificateFolder.appendingPathComponent("certificate.p12"))
105-
try provData.write(to: certificateFolder.appendingPathComponent("profile.mobileprovision"))
106-
try password.data(using: .utf8)?.write(to: certificateFolder.appendingPathComponent("password.txt"))
107-
try displayName.data(using: .utf8)?.write(to: certificateFolder.appendingPathComponent("name.txt"))
100+
try p12Data.write(to: folderURL.appendingPathComponent("certificate.p12"))
101+
try provData.write(to: folderURL.appendingPathComponent("profile.mobileprovision"))
102+
try password.data(using: .utf8)?.write(to: folderURL.appendingPathComponent("password.txt"))
103+
try displayName.data(using: .utf8)?.write(to: folderURL.appendingPathComponent("name.txt"))
108104

109-
return candidate
105+
return finalName
110106
}
111107

112108
func updateCertificate(folderName: String, p12Data: Data, provData: Data, password: String, displayName: String) throws {
113109
let certificateFolder = certificatesDirectory.appendingPathComponent(folderName)
114-
guard fileManager.fileExists(atPath: certificateFolder.path) else {
115-
throw NSError(domain: "CertificateFileManager", code: 1, userInfo: [NSLocalizedDescriptionKey: "Certificate folder not found"])
116-
}
117-
118110
let p12HashNew = CertificatesManager.sha256Hex(p12Data)
119111
let provHashNew = CertificatesManager.sha256Hex(provData)
120112
let passwordHashNew = CertificatesManager.sha256Hex(password.data(using: .utf8) ?? Data())
121113

122-
// Check if new version identical to any other existing (exclude self)
114+
// Prevent accidental duplicate update matching another cert
123115
let existingFolders = try fileManager.contentsOfDirectory(at: certificatesDirectory, includingPropertiesForKeys: nil, options: [.skipsHiddenFiles])
124-
for folder in existingFolders {
125-
if folder == certificateFolder { continue }
126-
116+
for folder in existingFolders where folder.lastPathComponent != folderName {
127117
let p12URL = folder.appendingPathComponent("certificate.p12")
128118
let provURL = folder.appendingPathComponent("profile.mobileprovision")
129119
let passwordURL = folder.appendingPathComponent("password.txt")
130-
131120
if fileManager.fileExists(atPath: p12URL.path) && fileManager.fileExists(atPath: provURL.path) && fileManager.fileExists(atPath: passwordURL.path) {
132121
do {
133122
let existingP12Data = try Data(contentsOf: p12URL)
@@ -167,10 +156,11 @@ class CertificateFileManager {
167156
}
168157
}
169158

159+
// MARK: - CertificateView (List + Add/Edit launchers)
170160
struct CertificateView: View {
171161
@State private var customCertificates: [CustomCertificate] = []
172162
@State private var showAddCertificateSheet = false
173-
@State private var editingCertificate: CustomCertificate? = nil
163+
@State private var editingCertificate: CustomCertificate? = nil // Used only for edit sheet (.sheet(item:))
174164
@State private var selectedCert: String? = nil
175165
@State private var showingDeleteAlert = false
176166
@State private var certToDelete: CustomCertificate?
@@ -198,7 +188,6 @@ struct CertificateView: View {
198188
.onTapGesture {
199189
// Only allow deselection if there are other certificates available
200190
if selectedCert == cert.folderName && customCertificates.count > 1 {
201-
// Find another certificate to select
202191
if let nextCert = customCertificates.first(where: { $0.folderName != cert.folderName }) {
203192
selectedCert = nextCert.folderName
204193
UserDefaults.standard.set(selectedCert, forKey: "selectedCertificateFolder")
@@ -211,8 +200,8 @@ struct CertificateView: View {
211200

212201
HStack {
213202
Button(action: {
203+
// EDIT: trigger identifiable sheet
214204
editingCertificate = cert
215-
showAddCertificateSheet = true
216205
}) {
217206
Image(systemName: "pencil")
218207
.foregroundColor(.blue)
@@ -225,7 +214,6 @@ struct CertificateView: View {
225214
Spacer()
226215

227216
Button(action: {
228-
// Prevent deletion if it's the only certificate
229217
if customCertificates.count > 1 {
230218
certToDelete = cert
231219
showingDeleteAlert = true
@@ -252,26 +240,25 @@ struct CertificateView: View {
252240
.toolbar {
253241
ToolbarItem(placement: .navigationBarTrailing) {
254242
Button(action: {
255-
editingCertificate = nil
256243
showAddCertificateSheet = true
257244
}) {
258245
Image(systemName: "plus")
259246
}
260247
}
261248
}
249+
// ADD sheet (new certificate only)
262250
.sheet(isPresented: $showAddCertificateSheet, onDismiss: {
263-
customCertificates = CertificateFileManager.shared.loadCertificates()
264-
editingCertificate = nil
265-
// Ensure at least one certificate is selected
266-
ensureSelection()
251+
reloadCertificatesAndEnsureSelection()
267252
}) {
268-
if let editingCertificate = editingCertificate {
269-
AddCertificateView(editingCertificate: editingCertificate)
270-
.presentationDetents([.large])
271-
} else {
272-
AddCertificateView()
273-
.presentationDetents([.large])
274-
}
253+
AddCertificateView()
254+
.presentationDetents([.large])
255+
}
256+
// EDIT sheet (identifiable)
257+
.sheet(item: $editingCertificate, onDismiss: {
258+
reloadCertificatesAndEnsureSelection()
259+
}) { editing in
260+
AddCertificateView(editingCertificate: editing)
261+
.presentationDetents([.large])
275262
}
276263
.alert("Delete Certificate?", isPresented: $showingDeleteAlert) {
277264
Button("Delete", role: .destructive) {
@@ -284,17 +271,18 @@ struct CertificateView: View {
284271
Text("Are you sure? This can't be undone.")
285272
}
286273
.onAppear {
287-
customCertificates = CertificateFileManager.shared.loadCertificates()
288-
selectedCert = UserDefaults.standard.string(forKey: "selectedCertificateFolder")
289-
290-
// Ensure at least one certificate is selected
291-
ensureSelection()
274+
reloadCertificatesAndEnsureSelection()
292275
}
293276
}
294277
}
295278

279+
private func reloadCertificatesAndEnsureSelection() {
280+
customCertificates = CertificateFileManager.shared.loadCertificates()
281+
selectedCert = UserDefaults.standard.string(forKey: "selectedCertificateFolder")
282+
ensureSelection()
283+
}
284+
296285
private func ensureSelection() {
297-
// If no certificate is selected or the selected one doesn't exist, select the first one
298286
if selectedCert == nil || !customCertificates.contains(where: { $0.folderName == selectedCert }) {
299287
if let firstCert = customCertificates.first {
300288
selectedCert = firstCert.folderName
@@ -307,7 +295,6 @@ struct CertificateView: View {
307295
try? CertificateFileManager.shared.deleteCertificate(folderName: cert.folderName)
308296
customCertificates = CertificateFileManager.shared.loadCertificates()
309297

310-
// If we're deleting the currently selected certificate, select another one
311298
if selectedCert == cert.folderName {
312299
if let newSelection = customCertificates.first {
313300
selectedCert = newSelection.folderName
@@ -317,12 +304,11 @@ struct CertificateView: View {
317304
UserDefaults.standard.removeObject(forKey: "selectedCertificateFolder")
318305
}
319306
}
320-
321-
// Ensure selection is maintained
322307
ensureSelection()
323308
}
324309
}
325310

311+
// MARK: - Add / Edit View
326312
struct AddCertificateView: View {
327313
@Environment(\.dismiss) private var dismiss
328314
let editingCertificate: CustomCertificate?
@@ -333,6 +319,7 @@ struct AddCertificateView: View {
333319
@State private var activeSheet: CertificatePickerKind?
334320
@State private var isChecking = false
335321
@State private var errorMessage = ""
322+
@State private var displayName: String = ""
336323
@State private var hasLoadedForEdit = false
337324

338325
init(editingCertificate: CustomCertificate? = nil) {
@@ -374,6 +361,11 @@ struct AddCertificateView: View {
374361
.disabled(isChecking)
375362
}
376363

364+
Section(header: Text("Display Name")) {
365+
TextField("Optional Display Name", text: $displayName)
366+
.disabled(isChecking)
367+
}
368+
377369
Section(header: Text("Password")) {
378370
SecureField("Enter Password", text: $password)
379371
.disabled(isChecking)
@@ -423,7 +415,6 @@ struct AddCertificateView: View {
423415
errorMessage = ""
424416
}
425417
.onAppear {
426-
// Load data for editing only once and only if we haven't already loaded it
427418
if let cert = editingCertificate, !hasLoadedForEdit {
428419
loadForEdit(cert: cert)
429420
hasLoadedForEdit = true
@@ -437,13 +428,17 @@ struct AddCertificateView: View {
437428
let p12URL = certFolder.appendingPathComponent("certificate.p12")
438429
let provURL = certFolder.appendingPathComponent("profile.mobileprovision")
439430
let passwordURL = certFolder.appendingPathComponent("password.txt")
431+
let nameURL = certFolder.appendingPathComponent("name.txt")
440432

441433
p12File = CertificateFileItem(name: "certificate.p12", url: p12URL)
442434
provFile = CertificateFileItem(name: "profile.mobileprovision", url: provURL)
443435

444436
if let pwData = try? Data(contentsOf: passwordURL), let pw = String(data: pwData, encoding: .utf8) {
445437
password = pw
446438
}
439+
if let nameData = try? Data(contentsOf: nameURL), let nameStr = String(data: nameData, encoding: .utf8) {
440+
displayName = nameStr
441+
}
447442
}
448443

449444
private func saveCertificate() {
@@ -457,32 +452,30 @@ struct AddCertificateView: View {
457452
var p12Data: Data
458453
var provData: Data
459454
if editingCertificate != nil {
460-
// For edit, files are in app container, no security scope needed
461455
p12Data = try Data(contentsOf: p12URL)
462456
provData = try Data(contentsOf: provURL)
463457
} else {
464-
// For new, access security-scoped
465-
guard p12URL.startAccessingSecurityScopedResource() else {
466-
throw NSError(domain: "AccessError", code: 1, userInfo: [NSLocalizedDescriptionKey: "Cannot access P12 file"])
458+
guard p12URL.startAccessingSecurityScopedResource(),
459+
provURL.startAccessingSecurityScopedResource() else {
460+
DispatchQueue.main.async {
461+
isChecking = false
462+
errorMessage = "Security-scoped resource access failed."
463+
}
464+
return
467465
}
468-
defer { p12URL.stopAccessingSecurityScopedResource() }
469-
470-
guard provURL.startAccessingSecurityScopedResource() else {
471-
throw NSError(domain: "AccessError", code: 2, userInfo: [NSLocalizedDescriptionKey: "Cannot access Provision file"])
466+
defer {
467+
p12URL.stopAccessingSecurityScopedResource()
468+
provURL.stopAccessingSecurityScopedResource()
472469
}
473-
defer { provURL.stopAccessingSecurityScopedResource() }
474-
475470
p12Data = try Data(contentsOf: p12URL)
476471
provData = try Data(contentsOf: provURL)
477472
}
478473

479-
let result = CertificatesManager.check(p12Data: p12Data, password: password, mobileProvisionData: provData)
480-
474+
let checkResult = CustomCertificatesManager.check(p12Data: p12Data, password: password, mobileProvisionData: provData)
481475
var dispatchError: String?
482-
switch result {
483-
case .success(.success):
484-
let displayName = CertificatesManager.getCertificateName(p12Data: p12Data, password: password) ?? "Custom Certificate"
485-
476+
477+
switch checkResult {
478+
case .success(.success(_, _)):
486479
if let folder = editingCertificate?.folderName {
487480
try CertificateFileManager.shared.updateCertificate(folderName: folder, p12Data: p12Data, provData: provData, password: password, displayName: displayName)
488481
} else {

0 commit comments

Comments
 (0)