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

Commit cea5fb9

Browse files
authored
Add download/sign feature
1 parent fbb8f04 commit cea5fb9

File tree

4 files changed

+288
-164
lines changed

4 files changed

+288
-164
lines changed

Sources/prostore/signing/DownloadSignManager.swift

Whitespace-only changes.
Lines changed: 50 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,15 @@
1+
// signer.swift
12
import Foundation
23
import ZIPFoundation
34
import ZsignSwift
4-
5+
56
public enum signer {
67
public static func sign(
78
ipaURL: URL,
89
p12URL: URL,
910
provURL: URL,
1011
p12Password: String,
11-
progressUpdate: @escaping (String) -> Void = { _ in },
12+
progressUpdate: @escaping (String, Double) -> Void = { _, _ in },
1213
completion: @escaping (Result<URL, Error>) -> Void
1314
) {
1415
SigningManager.sign(
@@ -47,19 +48,19 @@ public enum signer {
4748
return expDate
4849
}
4950
}
50-
51+
5152
fileprivate class SigningManager {
5253
static func sign(
5354
ipaURL: URL,
5455
p12URL: URL,
5556
provURL: URL,
5657
p12Password: String,
57-
progressUpdate: @escaping (String) -> Void,
58+
progressUpdate: @escaping (String, Double) -> Void,
5859
completion: @escaping (Result<URL, Error>) -> Void
5960
) {
6061
DispatchQueue.global(qos: .userInitiated).async {
6162
do {
62-
progressUpdate("Preparing files 📂")
63+
progressUpdate("Preparing files 📂", 0.0)
6364
let (tmpRoot, inputsDir, workDir) = try prepareTemporaryWorkspace()
6465
defer {
6566
cleanupTemporaryFiles(at: tmpRoot)
@@ -70,13 +71,16 @@ fileprivate class SigningManager {
7071
provURL: provURL,
7172
to: inputsDir
7273
)
73-
progressUpdate("Unzipping IPA 🔓")
74-
try extractIPA(ipaURL: localIPA, to: workDir, progressUpdate: progressUpdate)
74+
progressUpdate("Unzipping IPA 🔓", 0.25)
75+
try extractIPA(ipaURL: localIPA, to: workDir, progressUpdate: { status, progress in
76+
progressUpdate(status, 0.25 + (progress * 0.25))
77+
})
7578
let payloadDir = workDir.appendingPathComponent("Payload")
7679
let appDir = try findAppBundle(in: payloadDir)
77-
progressUpdate("Signing \(appDir.lastPathComponent) ✍️")
80+
progressUpdate("Signing \(appDir.lastPathComponent) ✍️", 0.5)
7881
let sema = DispatchSemaphore(value: 0)
7982
var signingError: Error?
83+
8084
// Use the ZsignSwift API
8185
_ = Zsign.sign(
8286
appPath: appDir.path,
@@ -90,23 +94,27 @@ fileprivate class SigningManager {
9094
sema.signal()
9195
}
9296
sema.wait()
97+
9398
if let error = signingError {
9499
throw error
95100
}
96-
progressUpdate("Zipping signed IPA 📦")
101+
102+
progressUpdate("Zipping signed IPA 📦", 0.75)
97103
let signedIPAURL = try createSignedIPA(
98104
from: workDir,
99105
originalIPAURL: ipaURL,
100106
outputDir: tmpRoot,
101-
progressUpdate: progressUpdate
107+
progressUpdate: { status, progress in
108+
progressUpdate(status, 0.75 + (progress * 0.25))
109+
}
102110
)
103111
completion(.success(signedIPAURL))
104112
} catch {
105113
completion(.failure(error))
106114
}
107115
}
108116
}
109-
117+
110118
// MARK: - workspace helpers
111119
static func prepareTemporaryWorkspace() throws -> (URL, URL, URL) {
112120
let fm = FileManager.default
@@ -117,7 +125,7 @@ fileprivate class SigningManager {
117125
try fm.createDirectory(at: work, withIntermediateDirectories: true)
118126
return (tmpRoot, inputs, work)
119127
}
120-
128+
121129
static func copyInputFiles(
122130
ipaURL: URL,
123131
p12URL: URL,
@@ -138,24 +146,24 @@ fileprivate class SigningManager {
138146
try fm.copyItem(at: provURL, to: localProv)
139147
return (localIPA, localP12, localProv)
140148
}
141-
149+
142150
static func extractIPA(
143151
ipaURL: URL,
144152
to workDir: URL,
145-
progressUpdate: @escaping (String) -> Void
153+
progressUpdate: @escaping (String, Double) -> Void
146154
) throws {
147155
let fm = FileManager.default
148156
let progress = Progress()
149157
let observation = progress.observe(\Progress.fractionCompleted) { prog, _ in
150158
let pct = Int(prog.fractionCompleted * 100)
151-
progressUpdate("Unzipping IPA 🔓 (\(pct)%)")
159+
progressUpdate("Unzipping IPA 🔓 (\(pct)%)", prog.fractionCompleted)
152160
}
153161
defer {
154162
observation.invalidate()
155163
}
156164
try fm.unzipItem(at: ipaURL, to: workDir, progress: progress)
157165
}
158-
166+
159167
static func findAppBundle(in payloadDir: URL) throws -> URL {
160168
let fm = FileManager.default
161169
guard fm.fileExists(atPath: payloadDir.path) else {
@@ -167,37 +175,51 @@ fileprivate class SigningManager {
167175
}
168176
return payloadDir.appendingPathComponent(appName)
169177
}
170-
178+
171179
static func createSignedIPA(
172180
from workDir: URL,
173181
originalIPAURL: URL,
174182
outputDir: URL,
175-
progressUpdate: @escaping (String) -> Void
183+
progressUpdate: @escaping (String, Double) -> Void
176184
) throws -> URL {
177185
let fm = FileManager.default
178186
let originalBase = originalIPAURL.deletingPathExtension().lastPathComponent
179-
let finalFileName = "\(originalBase)_signed_\(UUID().uuidString).ipa"
187+
let finalFileName = "signed_\(UUID().uuidString).ipa"
180188
let signedIpa = outputDir.appendingPathComponent(finalFileName)
181189
let progress = Progress()
182190
let observation = progress.observe(\Progress.fractionCompleted) { prog, _ in
183191
let pct = Int(prog.fractionCompleted * 100)
184-
progressUpdate("Zipping signed IPA 📦 (\(pct)%)")
192+
progressUpdate("Zipping signed IPA 📦 (\(pct)%)", prog.fractionCompleted)
185193
}
186194
defer {
187195
observation.invalidate()
188196
}
189197
try fm.zipItem(at: workDir, to: signedIpa, shouldKeepParent: false, progress: progress)
190-
// Copy to Documents for sharing
191-
let docs = fm.urls(for: .documentDirectory, in: .userDomainMask).first!
192-
let outURL = docs.appendingPathComponent(finalFileName)
193-
if fm.fileExists(atPath: outURL.path) {
194-
try fm.removeItem(at: outURL)
198+
199+
// Copy to AppFolder/temp for permanent storage
200+
let appFolder = getAppFolder()
201+
let tempDir = appFolder.appendingPathComponent("temp")
202+
try fm.createDirectory(at: tempDir, withIntermediateDirectories: true)
203+
let finalURL = tempDir.appendingPathComponent(finalFileName)
204+
if fm.fileExists(atPath: finalURL.path) {
205+
try fm.removeItem(at: finalURL)
195206
}
196-
try fm.copyItem(at: signedIpa, to: outURL)
197-
return outURL
207+
try fm.copyItem(at: signedIpa, to: finalURL)
208+
209+
return finalURL
198210
}
199-
211+
200212
static func cleanupTemporaryFiles(at url: URL) {
201213
try? FileManager.default.removeItem(at: url)
202214
}
215+
216+
private static func getAppFolder() -> URL {
217+
let fm = FileManager.default
218+
let documents = fm.urls(for: .documentDirectory, in: .userDomainMask).first!
219+
let appFolder = documents.appendingPathComponent("AppFolder")
220+
if !fm.fileExists(atPath: appFolder.path) {
221+
try? fm.createDirectory(at: appFolder, withIntermediateDirectories: true)
222+
}
223+
return appFolder
224+
}
203225
}

0 commit comments

Comments
 (0)