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

Commit 8342507

Browse files
authored
Add AppleP12 Credit and remove loyahdev certs and credit.
1 parent ba13700 commit 8342507

File tree

3 files changed

+5
-309
lines changed

3 files changed

+5
-309
lines changed

Sources/prostore/certificates/OfficialCertsDownloader.swift

Lines changed: 1 addition & 292 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import SwiftUI
22
import ZIPFoundation
33

4-
// MARK: - Release Models (for Loyahdev)
4+
// MARK: - Release Models
55
struct Release: Codable, Identifiable, Equatable, Hashable {
66
let id: Int
77
let name: String
@@ -47,297 +47,6 @@ struct BlobResponse: Codable {
4747
let content: String?
4848
}
4949

50-
// MARK: - Loyahdev Certificates View
51-
struct LoyahdevCertificatesView: View {
52-
@Environment(\.dismiss) private var dismiss
53-
@State private var releases: [Release] = []
54-
@State private var selectedRelease: Release? = nil
55-
@State private var statusMessage = ""
56-
@State private var isChecking = false
57-
@State private var isLoadingReleases = true
58-
@State private var p12Data: Data? = nil
59-
@State private var provData: Data? = nil
60-
@State private var password: String? = nil
61-
@State private var displayName = ""
62-
@State private var expiry: Date? = nil
63-
64-
private var isSuccess: Bool {
65-
statusMessage.contains("Success")
66-
}
67-
68-
private var statusColor: Color {
69-
if statusMessage.contains("Downloading") {
70-
return .yellow
71-
} else if isSuccess {
72-
return .green
73-
} else {
74-
return .red
75-
}
76-
}
77-
78-
private let dateFormatter: DateFormatter = {
79-
let f = DateFormatter()
80-
f.dateStyle = .medium
81-
return f
82-
}()
83-
84-
var body: some View {
85-
NavigationStack {
86-
Form {
87-
Section("Select Loyahdev Certificate") {
88-
Picker("Certificate", selection: $selectedRelease) {
89-
if isLoadingReleases {
90-
Text("-- Loading --").tag(nil as Release?)
91-
} else {
92-
Text("-- Select a certificate --").tag(nil as Release?)
93-
ForEach(releases) { release in
94-
Text(cleanName(release.name)).tag(release as Release?)
95-
}
96-
}
97-
}
98-
}
99-
Section {
100-
Text("Provided by loyahdev")
101-
.font(.caption)
102-
.foregroundColor(.secondary)
103-
}
104-
if let release = selectedRelease {
105-
Section("Details") {
106-
Text("Tag: \(release.tagName)")
107-
if !statusMessage.isEmpty {
108-
Text(statusMessage)
109-
.foregroundColor(statusColor)
110-
}
111-
Text("Published: \(dateFormatter.string(from: isoDate(string: release.publishedAt)))")
112-
if let exp = expiry {
113-
expiryDisplay(for: exp)
114-
}
115-
}
116-
}
117-
Section {
118-
Button("Add Certificate") {
119-
addCertificate()
120-
}
121-
.disabled(p12Data == nil || provData == nil || password == nil || isChecking)
122-
}
123-
}
124-
.navigationTitle("Loyahdev Certificates")
125-
.navigationBarTitleDisplayMode(.inline)
126-
.navigationBarItems(leading:
127-
Button("×") {
128-
dismiss()
129-
}
130-
)
131-
.onAppear {
132-
fetchReleases()
133-
}
134-
.onChange(of: selectedRelease) { newValue in
135-
if newValue != nil && !isChecking {
136-
clearCertificateData()
137-
checkCertificate()
138-
} else if newValue == nil {
139-
clearCertificateData()
140-
}
141-
}
142-
}
143-
}
144-
145-
private func clearCertificateData() {
146-
statusMessage = ""
147-
expiry = nil
148-
p12Data = nil
149-
provData = nil
150-
password = nil
151-
displayName = ""
152-
}
153-
154-
private func expiryDisplay(for expiry: Date) -> some View {
155-
let now = Date()
156-
let components = Calendar.current.dateComponents([.day], from: now, to: expiry)
157-
let days = components.day ?? 0
158-
let displayDate = expiry.formattedWithOrdinal()
159-
let expiryText: String
160-
let expiryColor: Color
161-
if days > 0 {
162-
expiryText = "Expires on the \(displayDate)"
163-
expiryColor = .green
164-
} else {
165-
expiryText = "Expired on the \(displayDate)"
166-
expiryColor = .red
167-
}
168-
return Text(expiryText)
169-
.foregroundColor(expiryColor)
170-
.font(.caption)
171-
}
172-
173-
private func isoDate(string: String) -> Date {
174-
let formatter = ISO8601DateFormatter()
175-
return formatter.date(from: string) ?? Date()
176-
}
177-
178-
private func cleanName(_ name: String) -> String {
179-
name.replacingOccurrences(of: "\\\\", with: "").replacingOccurrences(of: "\\", with: "")
180-
}
181-
182-
private func getPAT() async -> String? {
183-
guard let url = URL(string: "https://certapi.loyah.dev/pac") else { return nil }
184-
do {
185-
let (data, _) = try await URLSession.shared.data(from: url)
186-
return String(data: data, encoding: .utf8)?.trimmingCharacters(in: .whitespacesAndNewlines)
187-
} catch {
188-
return nil
189-
}
190-
}
191-
192-
private func fetchReleases() {
193-
Task {
194-
let pat = await getPAT()
195-
let url = URL(string: "https://api.github.com/repos/loyahdev/certificates/releases")!
196-
var request = URLRequest(url: url)
197-
if let pat = pat {
198-
request.setValue("token \(pat)", forHTTPHeaderField: "Authorization")
199-
}
200-
do {
201-
let (data, response) = try await URLSession.shared.data(for: request)
202-
var decodeData = data
203-
if let httpResponse = response as? HTTPURLResponse, httpResponse.statusCode != 200, pat != nil {
204-
let fallbackRequest = URLRequest(url: url)
205-
let (fallbackData, _) = try await URLSession.shared.data(for: fallbackRequest)
206-
decodeData = fallbackData
207-
}
208-
let decoder = JSONDecoder()
209-
decoder.dateDecodingStrategy = .deferredToDate
210-
let decoded = try decoder.decode([Release].self, from: decodeData)
211-
await MainActor.run {
212-
self.releases = decoded.sorted { isoDate(string: $0.publishedAt) > isoDate(string: $1.publishedAt) }
213-
self.isLoadingReleases = false
214-
}
215-
} catch {
216-
await MainActor.run {
217-
self.statusMessage = "Failed to fetch releases: \(error.localizedDescription)"
218-
self.isLoadingReleases = false
219-
}
220-
}
221-
}
222-
}
223-
224-
private func findCertificateFiles(in directory: URL) throws -> (p12Urls: [URL], provUrls: [URL]) {
225-
var p12Urls: [URL] = []
226-
var provUrls: [URL] = []
227-
if let enumerator = FileManager.default.enumerator(at: directory, includingPropertiesForKeys: nil, options: [.skipsHiddenFiles, .skipsSubdirectoryDescendants]) {
228-
for case let fileURL as URL in enumerator {
229-
let path = fileURL.path
230-
if !path.contains("__MACOSX") {
231-
if path.hasSuffix(".p12") {
232-
p12Urls.append(fileURL)
233-
} else if path.hasSuffix(".mobileprovision") {
234-
provUrls.append(fileURL)
235-
}
236-
}
237-
}
238-
}
239-
return (p12Urls, provUrls)
240-
}
241-
242-
private func checkCertificate() {
243-
guard let release = selectedRelease,
244-
let asset = release.assets.first(where: { $0.name.hasSuffix(".zip") }),
245-
let downloadUrl = URL(string: asset.browserDownloadUrl) else {
246-
statusMessage = "Invalid release"
247-
return
248-
}
249-
isChecking = true
250-
statusMessage = "Downloading..."
251-
Task {
252-
let pat = await getPAT()
253-
var downloadRequest = URLRequest(url: downloadUrl)
254-
if let pat = pat {
255-
downloadRequest.setValue("token \(pat)", forHTTPHeaderField: "Authorization")
256-
}
257-
do {
258-
var tempData = Data()
259-
var response = URLResponse()
260-
(tempData, response) = try await URLSession.shared.data(for: downloadRequest)
261-
if let httpResponse = response as? HTTPURLResponse, httpResponse.statusCode != 200, pat != nil {
262-
let fallbackRequest = URLRequest(url: downloadUrl)
263-
(tempData, _) = try await URLSession.shared.data(for: fallbackRequest)
264-
}
265-
let tempDir = FileManager.default.temporaryDirectory.appendingPathComponent(UUID().uuidString)
266-
try FileManager.default.createDirectory(at: tempDir, withIntermediateDirectories: true, attributes: nil)
267-
defer {
268-
try? FileManager.default.removeItem(at: tempDir)
269-
}
270-
let zipPath = tempDir.appendingPathComponent("temp.zip")
271-
try tempData.write(to: zipPath)
272-
let extractDir = tempDir.appendingPathComponent("extracted")
273-
try FileManager.default.unzipItem(at: zipPath, to: extractDir, progress: nil)
274-
// Find files
275-
let (p12Urls, provUrls) = try findCertificateFiles(in: extractDir)
276-
guard p12Urls.count == 1, provUrls.count == 1 else {
277-
throw NSError(domain: "Extraction", code: 1, userInfo: [NSLocalizedDescriptionKey: "Unable to extract certificate"])
278-
}
279-
let p12Url = p12Urls[0]
280-
let provUrl = provUrls[0]
281-
let p12DataLocal = try Data(contentsOf: p12Url)
282-
let provDataLocal = try Data(contentsOf: provUrl)
283-
var successPw: String?
284-
for pwCandidate in ["Hydrogen", "Sideloadingdotorg", "nocturnacerts"] {
285-
switch CertificatesManager.check(p12Data: p12DataLocal, password: pwCandidate, mobileProvisionData: provDataLocal) {
286-
case .success(.success):
287-
successPw = pwCandidate
288-
break
289-
default:
290-
break
291-
}
292-
}
293-
guard let pw = successPw else {
294-
throw NSError(domain: "Password", code: 1, userInfo: [NSLocalizedDescriptionKey: "Password check failed"])
295-
}
296-
let exp = signer.getExpirationDate(provData: provDataLocal)
297-
let dispName = CertificatesManager.getCertificateName(mobileProvisionData: provDataLocal) ?? cleanName(release.name)
298-
await MainActor.run {
299-
self.p12Data = p12DataLocal
300-
self.provData = provDataLocal
301-
self.password = pw
302-
self.displayName = dispName
303-
self.expiry = exp
304-
self.statusMessage = "Success: Ready to add \(dispName)"
305-
self.isChecking = false
306-
}
307-
} catch {
308-
await MainActor.run {
309-
self.statusMessage = "Error: \(error.localizedDescription)"
310-
self.isChecking = false
311-
}
312-
}
313-
}
314-
}
315-
316-
private func addCertificate() {
317-
guard let p12DataLocal = p12Data,
318-
let provDataLocal = provData,
319-
let pw = password,
320-
!displayName.isEmpty else { return }
321-
isChecking = true
322-
statusMessage = "Adding..."
323-
Task {
324-
do {
325-
_ = try CertificateFileManager.shared.saveCertificate(p12Data: p12DataLocal, provData: provDataLocal, password: pw, displayName: displayName)
326-
await MainActor.run {
327-
self.statusMessage = "Added successfully"
328-
self.isChecking = false
329-
self.dismiss()
330-
}
331-
} catch {
332-
await MainActor.run {
333-
self.statusMessage = "Error adding: \(error.localizedDescription)"
334-
self.isChecking = false
335-
}
336-
}
337-
}
338-
}
339-
}
340-
34150
// MARK: - Official Certificates View
34251
struct OfficialCertificatesView: View {
34352
@Environment(\.dismiss) private var dismiss

Sources/prostore/views/AboutView.swift

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -30,10 +30,10 @@ struct AboutView: View {
3030
avatarURL: URL(string: "https://github.com/khcrysalis.png")!
3131
),
3232
Credit(
33-
name: "Loyahdev",
34-
role: "iOS Certificates Source",
35-
profileURL: URL(string: "https://github.com/loyahdev")!,
36-
avatarURL: URL(string: "https://github.com/loyahdev.png")!
33+
name: "AppleP12",
34+
role: "Certificate Status Check",
35+
profileURL: URL(string: "https://check-p12.applep12.com/")!,
36+
avatarURL: URL(string: "https://applep12.com/favicon/apple-touch-icon.png")!
3737
)
3838
]
3939

Sources/prostore/views/CertificateView.swift

Lines changed: 0 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,6 @@ struct CertificateView: View {
4545
@State private var certExpiries: [String: Date?] = [:]
4646
@State private var certStatuses: [String: String] = [:]
4747
@State private var showAddCertificateSheet = false
48-
@State private var showLoyahdevSheet = false
4948
@State private var showOfficialSheet = false
5049
@State private var editingCertificate: CustomCertificate? = nil // Used only for edit sheet (.sheet(item:))
5150
@State private var selectedCert: String? = nil
@@ -69,11 +68,6 @@ struct CertificateView: View {
6968
} label: {
7069
Label("Add from Files", systemImage: "folder.badge.plus")
7170
}
72-
Button {
73-
showLoyahdevSheet = true
74-
} label: {
75-
Label("Add from Loyahdev", systemImage: "globe")
76-
}
7771
Button {
7872
showOfficialSheet = true
7973
} label: {
@@ -95,13 +89,6 @@ struct CertificateView: View {
9589
AddCertificateView(onSave: { newlyAddedFolder = $0 })
9690
.presentationDetents([.large])
9791
}
98-
// Loyahdev sheet
99-
.sheet(isPresented: $showLoyahdevSheet, onDismiss: {
100-
reloadCertificatesAndEnsureSelection()
101-
}) {
102-
LoyahdevCertificatesView()
103-
.presentationDetents([.large])
104-
}
10592
// Official sheet
10693
.sheet(isPresented: $showOfficialSheet, onDismiss: {
10794
reloadCertificatesAndEnsureSelection()

0 commit comments

Comments
 (0)