import Sparkle import Cocoa extension UpdateDriver: SPUUpdaterDelegate { func feedURLString(for updater: SPUUpdater) -> String? { guard let appDelegate = NSApplication.shared.delegate as? AppDelegate else { return nil } // Sparkle supports a native concept of "channels" but it requires that // you share a single appcast file. We don't want to do that so we // do this instead. switch (appDelegate.ghostty.config.autoUpdateChannel) { case .tip: return "https://tip.files.ghostty.org/appcast.xml" case .stable: return "https://release.files.ghostty.org/appcast.xml" } } /// Called when an update is scheduled to install silently, /// which occurs when `auto-update = download`. /// /// When `auto-update = check`, Sparkle will call the corresponding /// delegate method on the responsible driver instead. func updater(_ updater: SPUUpdater, willInstallUpdateOnQuit item: SUAppcastItem, immediateInstallationBlock immediateInstallHandler: @escaping () -> Void) -> Bool { viewModel.state = .installing(.init(isAutoUpdate: true, retryTerminatingApplication: immediateInstallHandler)) return true } func updaterWillRelaunchApplication(_ updater: SPUUpdater) { // When the updater is relaunching the application we want to get macOS // to invalidate and re-encode all of our restorable state so that when // we relaunch it uses it. NSApp.invalidateRestorableState() for window in NSApp.windows { window.invalidateRestorableState() } } }