109 lines
3.7 KiB
Swift
109 lines
3.7 KiB
Swift
import AppKit
|
|
import Sparkle
|
|
|
|
@MainActor
|
|
final class Updater: NSObject, SPUUserDriver, SPUUpdaterDelegate {
|
|
static let shared = Updater()
|
|
private var updater: SPUUpdater?
|
|
private var now = false
|
|
private var staged: (item: SUAppcastItem, install: () -> Void)?
|
|
|
|
func start() {
|
|
let updater = SPUUpdater(hostBundle: .main, applicationBundle: .main, userDriver: self, delegate: self)
|
|
self.updater = updater
|
|
do {
|
|
try updater.start()
|
|
} catch {
|
|
tell("Updates are off", error.localizedDescription)
|
|
}
|
|
}
|
|
|
|
func check() {
|
|
if let staged {
|
|
if ask(staged.item) {
|
|
staged.install()
|
|
}
|
|
return
|
|
}
|
|
updater?.checkForUpdates()
|
|
}
|
|
|
|
private func ask(_ item: SUAppcastItem) -> Bool {
|
|
let current = Bundle.main.object(forInfoDictionaryKey: "CFBundleShortVersionString") as? String ?? ""
|
|
let alert = NSAlert()
|
|
alert.messageText = "Byre \(item.displayVersionString) is available"
|
|
alert.informativeText = "You have \(current)."
|
|
alert.addButton(withTitle: "Update Now")
|
|
alert.addButton(withTitle: "Update on Quit")
|
|
NSApp.activate()
|
|
return alert.runModal() == .alertFirstButtonReturn
|
|
}
|
|
|
|
private func tell(_ message: String, _ info: String) {
|
|
let alert = NSAlert()
|
|
alert.messageText = message
|
|
alert.informativeText = info
|
|
NSApp.activate()
|
|
alert.runModal()
|
|
}
|
|
|
|
func updater(_ updater: SPUUpdater, willInstallUpdateOnQuit item: SUAppcastItem, immediateInstallationBlock immediateInstallHandler: @escaping () -> Void) -> Bool {
|
|
staged = (item, immediateInstallHandler)
|
|
if ask(item) {
|
|
immediateInstallHandler()
|
|
}
|
|
return true
|
|
}
|
|
|
|
func show(_ request: SPUUpdatePermissionRequest, reply: @escaping (SUUpdatePermissionResponse) -> Void) {
|
|
reply(SUUpdatePermissionResponse(automaticUpdateChecks: true, sendSystemProfile: false))
|
|
}
|
|
|
|
func showUserInitiatedUpdateCheck(cancellation: @escaping () -> Void) {}
|
|
|
|
func showUpdateFound(with appcastItem: SUAppcastItem, state: SPUUserUpdateState, reply: @escaping (SPUUserUpdateChoice) -> Void) {
|
|
now = ask(appcastItem)
|
|
if !now && state.stage == .installing {
|
|
reply(.dismiss)
|
|
return
|
|
}
|
|
reply(.install)
|
|
}
|
|
|
|
func showUpdateReleaseNotes(with downloadData: SPUDownloadData) {}
|
|
|
|
func showUpdateReleaseNotesFailedToDownloadWithError(_ error: any Error) {}
|
|
|
|
func showUpdateNotFoundWithError(_ error: any Error, acknowledgement: @escaping () -> Void) {
|
|
tell(error.localizedDescription, (error as NSError).localizedRecoverySuggestion ?? "")
|
|
acknowledgement()
|
|
}
|
|
|
|
func showUpdaterError(_ error: any Error, acknowledgement: @escaping () -> Void) {
|
|
tell("Update failed", error.localizedDescription)
|
|
acknowledgement()
|
|
}
|
|
|
|
func showDownloadInitiated(cancellation: @escaping () -> Void) {}
|
|
|
|
func showDownloadDidReceiveExpectedContentLength(_ expectedContentLength: UInt64) {}
|
|
|
|
func showDownloadDidReceiveData(ofLength length: UInt64) {}
|
|
|
|
func showDownloadDidStartExtractingUpdate() {}
|
|
|
|
func showExtractionReceivedProgress(_ progress: Double) {}
|
|
|
|
func showReady(toInstallAndRelaunch reply: @escaping (SPUUserUpdateChoice) -> Void) {
|
|
reply(now ? .install : .dismiss)
|
|
}
|
|
|
|
func showInstallingUpdate(withApplicationTerminated applicationTerminated: Bool, retryTerminatingApplication: @escaping () -> Void) {}
|
|
|
|
func showUpdateInstalledAndRelaunched(_ relaunched: Bool, acknowledgement: @escaping () -> Void) {
|
|
acknowledgement()
|
|
}
|
|
|
|
func dismissUpdateInstallation() {}
|
|
}
|