WP-0: SwiftPM scaffold, shared model, signed app bundle script

This commit is contained in:
2026-08-30 20:56:37 +04:00
parent c06b8da052
commit 97d21be470
12 changed files with 495 additions and 2 deletions
@@ -0,0 +1,65 @@
import Foundation
/// The only type in the app that knows the on-disk directory layout.
public struct AppSupportPaths: Sendable {
public let root: URL
public let spool: URL
public let archive: URL
public let outbox: URL
public let watchFolder: URL
/// Production paths.
public static func standard() throws -> AppSupportPaths {
let fileManager = FileManager.default
let appSupportParent = try fileManager.url(
for: .applicationSupportDirectory,
in: .userDomainMask,
appropriateFor: nil,
create: true
)
let desktop = try fileManager.url(
for: .desktopDirectory,
in: .userDomainMask,
appropriateFor: nil,
create: true
)
let downloads = try fileManager.url(
for: .downloadsDirectory,
in: .userDomainMask,
appropriateFor: nil,
create: true
)
let root = appSupportParent.appendingPathComponent("Shotdeck", isDirectory: true)
return try AppSupportPaths(root: root, outbox: desktop, watchFolder: downloads)
}
/// Test paths rooted anywhere. Every directory is created if missing.
public init(root: URL, outbox: URL, watchFolder: URL) throws {
self.root = root
self.spool = root.appendingPathComponent("spool", isDirectory: true)
self.archive = root.appendingPathComponent("archive", isDirectory: true)
self.outbox = outbox
self.watchFolder = watchFolder
try Self.createDirectory(self.root)
try Self.createDirectory(self.spool)
try Self.createDirectory(self.archive)
try Self.createDirectory(self.outbox)
try Self.createDirectory(self.watchFolder)
}
public func sessionDirectory(_ id: UUID) -> URL {
spool.appendingPathComponent(id.uuidString, isDirectory: true)
}
public func archiveDirectory(_ id: UUID) -> URL {
archive.appendingPathComponent(id.uuidString, isDirectory: true)
}
private static func createDirectory(_ url: URL) throws {
try FileManager.default.createDirectory(
at: url,
withIntermediateDirectories: true
)
}
}
@@ -0,0 +1,40 @@
import Foundation
public enum DubaiTime {
private static let stampFormatter = LockedDateFormatter(dateFormat: "d MMM yyyy, HH:mm 'Dubai'")
private static let fileStampFormatter = LockedDateFormatter(dateFormat: "yyyyMMdd-HHmmss")
public static func stamp(_ date: Date) -> String {
stampFormatter.string(from: date)
}
public static func fileStamp(_ date: Date) -> String {
fileStampFormatter.string(from: date)
}
}
/// DateFormatter is not Sendable. This holder is the only shared mutable state
/// around a formatter: every read is serialized by the lock, so concurrent
/// calls (one per PDF page) cannot race.
private final class LockedDateFormatter: @unchecked Sendable {
private let lock = NSLock()
private let formatter: DateFormatter
init(dateFormat: String) {
let formatter = DateFormatter()
formatter.calendar = Calendar(identifier: .gregorian)
formatter.locale = Locale(identifier: "en_GB")
guard let dubai = TimeZone(identifier: "Asia/Dubai") else {
preconditionFailure("The Asia/Dubai time zone is missing from this system.")
}
formatter.timeZone = dubai
formatter.dateFormat = dateFormat
self.formatter = formatter
}
func string(from date: Date) -> String {
lock.lock()
defer { lock.unlock() }
return formatter.string(from: date)
}
}