- Add published logMessages array to SparkleUpdater to track all update events - Display logs in the Updates preferences tab with Show/Hide toggle - Each log entry is timestamped and shows both info and error messages - Logs persist during session with max 100 entries - Users can clear logs manually - Helps diagnose update failures directly in the app UI
124 lines
4.2 KiB
Swift
124 lines
4.2 KiB
Swift
import Sparkle
|
|
import Foundation
|
|
import OSLog
|
|
|
|
@MainActor
|
|
final class SparkleUpdater: NSObject, ObservableObject {
|
|
private lazy var controller: SPUStandardUpdaterController = {
|
|
SPUStandardUpdaterController(startingUpdater: true, updaterDelegate: self, userDriverDelegate: nil)
|
|
}()
|
|
private let logger = Logger(subsystem: "net.24unix.iKeyMon", category: "Sparkle")
|
|
private let verboseLogging: Bool
|
|
@Published var logMessages: [String] = []
|
|
@Published var showLogs: Bool = false
|
|
|
|
override init() {
|
|
self.verboseLogging = ProcessInfo.processInfo.environment["SPARKLE_VERBOSE_LOGGING"] == "1"
|
|
super.init()
|
|
_ = controller
|
|
log("Sparkle updater initialized (verbose=\(verboseLogging)).")
|
|
}
|
|
|
|
var automaticallyChecksForUpdates: Bool {
|
|
get { controller.updater.automaticallyChecksForUpdates }
|
|
set { controller.updater.automaticallyChecksForUpdates = newValue }
|
|
}
|
|
|
|
var automaticallyDownloadsUpdates: Bool {
|
|
get { controller.updater.automaticallyDownloadsUpdates }
|
|
set { controller.updater.automaticallyDownloadsUpdates = newValue }
|
|
}
|
|
|
|
func checkForUpdates() {
|
|
log("Manual check for updates triggered.")
|
|
controller.checkForUpdates(nil)
|
|
}
|
|
|
|
private func log(_ message: String) {
|
|
logger.log("\(message, privacy: .public)")
|
|
addLogMessage("[INFO] \(message)")
|
|
if verboseLogging {
|
|
print("[Sparkle] \(message)")
|
|
}
|
|
}
|
|
|
|
private func logError(_ message: String) {
|
|
logger.error("\(message, privacy: .public)")
|
|
addLogMessage("[ERROR] \(message)")
|
|
if verboseLogging {
|
|
fputs("[Sparkle][error] \(message)\n", stderr)
|
|
}
|
|
}
|
|
|
|
private func addLogMessage(_ message: String) {
|
|
let timestamp = DateFormatter.localizedString(from: Date(), dateStyle: .none, timeStyle: .medium)
|
|
let timestampedMessage = "[\(timestamp)] \(message)"
|
|
logMessages.append(timestampedMessage)
|
|
if logMessages.count > 100 {
|
|
logMessages.removeFirst()
|
|
}
|
|
}
|
|
|
|
private func describe(update item: SUAppcastItem) -> String {
|
|
let short = item.displayVersionString
|
|
let build = item.versionString
|
|
return "\(short) (build \(build))"
|
|
}
|
|
}
|
|
|
|
extension SparkleUpdater: SPUUpdaterDelegate {
|
|
nonisolated func updater(_ updater: SPUUpdater, didFinishLoading appcast: SUAppcast) {
|
|
Task { @MainActor in
|
|
log("Loaded Sparkle appcast containing \(appcast.items.count) item(s).")
|
|
}
|
|
}
|
|
|
|
nonisolated func updater(_ updater: SPUUpdater, didFindValidUpdate item: SUAppcastItem) {
|
|
Task { @MainActor in
|
|
log("Found valid update \(describe(update: item))")
|
|
}
|
|
}
|
|
|
|
nonisolated func updaterDidNotFindUpdate(_ updater: SPUUpdater) {
|
|
Task { @MainActor in
|
|
log("No updates available.")
|
|
}
|
|
}
|
|
|
|
nonisolated func updater(_ updater: SPUUpdater, willDownloadUpdate item: SUAppcastItem, with request: NSMutableURLRequest) {
|
|
Task { @MainActor in
|
|
log("Downloading \(describe(update: item)) from \(request.url?.absoluteString ?? "unknown URL")")
|
|
}
|
|
}
|
|
|
|
nonisolated func updater(_ updater: SPUUpdater, didDownloadUpdate item: SUAppcastItem) {
|
|
Task { @MainActor in
|
|
log("Finished downloading \(describe(update: item))")
|
|
}
|
|
}
|
|
|
|
nonisolated func updater(_ updater: SPUUpdater, failedToDownloadUpdate item: SUAppcastItem, error: Error) {
|
|
Task { @MainActor in
|
|
logError("Failed to download \(describe(update: item)): \(error.localizedDescription)")
|
|
}
|
|
}
|
|
|
|
nonisolated func userDidCancelDownload(_ updater: SPUUpdater) {
|
|
Task { @MainActor in
|
|
log("User cancelled Sparkle download.")
|
|
}
|
|
}
|
|
|
|
nonisolated func updater(_ updater: SPUUpdater, willInstallUpdate item: SUAppcastItem) {
|
|
Task { @MainActor in
|
|
log("Will install update \(describe(update: item))")
|
|
}
|
|
}
|
|
|
|
nonisolated func updater(_ updater: SPUUpdater, didAbortWithError error: Error) {
|
|
Task { @MainActor in
|
|
logError("Sparkle aborted: \(error.localizedDescription)")
|
|
}
|
|
}
|
|
}
|