rastrillo / native Public

Clone
git clone https://amadan.net/rastrillo/native

Plain git — no account needed to clone.

Download

Download this file

1#if os(macOS)
2import Foundation
3
4/// How an app's updater behaves between the checks a person asks for.
5///
6/// `standard` is Sparkle's own flow: it asks before checking automatically and
7/// shows its update alert when a scheduled check finds something. `quiet` never
8/// opens a window for a scheduled check. It downloads in the background, installs
9/// when the app quits, and publishes `NativeUpdater.pendingUpdate` so the app can
10/// show a small notice. While the app reports itself busy (`NativeUpdater.isBusy`,
11/// a call, say) scheduled checks wait until it is idle again, so a download never
12/// competes with the busy work. A check the person asks for always behaves normally.
13///
14/// Quiet also needs the bundle packaged with `"install": "automatic"` in its
15/// `mac-updates.json`: that is what turns on automatic checks and downloads
16/// without Sparkle's first-launch question. See docs/mac-updates.md.
17public enum NativeUpdatePolicy: Sendable, Equatable {
18 case standard
19 case quiet
20}
21
22/// What a quiet updater has found on a schedule and not yet installed.
23public enum NativePendingUpdate: Sendable, Equatable {
24 case none
25 /// Found but not downloaded, because the person turned automatic updates off
26 /// or the update needs their attention first. Check for Updates shows it.
27 case available(version: String)
28 /// Downloaded and staged. It installs when the app quits, or at once through
29 /// `NativeUpdater.installPendingUpdate()`.
30 case ready(version: String)
31}
32
33/// Which kind of check Sparkle is about to start, without importing Sparkle.
34enum NativeUpdateCheck: Sendable {
35 case userInitiated, background, information
36}
37
38/// The quiet policy's decisions, kept apart from Sparkle so that they can be tested
39/// without a feed, a signed bundle or a network.
40struct QuietUpdateRules: Sendable, Equatable {
41 private(set) var pending: NativePendingUpdate = .none
42 private(set) var busy = false
43 /// A scheduled check was declined while busy. Sparkle records the declined check
44 /// as done and waits a full interval, so without this the update would wait a
45 /// day for a call that lasted ten minutes.
46 private(set) var deferredCheck = false
47
48 /// Only scheduled checks wait. A person who chooses Check for Updates mid-call
49 /// asked for it.
50 mutating func mayPerform(_ check: NativeUpdateCheck) -> Bool {
51 guard check == .background, busy else { return true }
52 deferredCheck = true
53 return false
54 }
55
56 /// Returns true when a check deferred while busy should run now.
57 mutating func setBusy(_ newValue: Bool) -> Bool {
58 busy = newValue
59 guard !newValue, deferredCheck else { return false }
60 deferredCheck = false
61 return true
62 }
63
64 mutating func staged(version: String) { pending = .ready(version: version) }
65
66 /// A scheduled update Sparkle would have shown in a window. One already
67 /// downloaded is ready; otherwise the notice offers it.
68 mutating func found(version: String, downloaded: Bool) {
69 pending = downloaded ? .ready(version: version) : .available(version: version)
70 }
71
72 /// Sparkle's own window has the update now, or the session is over.
73 mutating func clear() { pending = .none }
74
75 /// Acting on a pending update either relaunches the app or opens Sparkle's
76 /// window, so neither happens while busy.
77 var canInstall: Bool { pending != .none && !busy }
78}
79#endif
80