Skip to content

Commit 830d147

Browse files
feat: support disabling the built-in updater (#219)
Closes #182. UserDefaults can be forcibly set by MDM admins. When the `disableUpdater` bool in UserDefaults is set to `false`, the updater won't be initialized on launch, and the UI elements for the updater in settings will be hidden. Related to #220.
1 parent de4b0e5 commit 830d147

File tree

4 files changed

+56
-32
lines changed

4 files changed

+56
-32
lines changed

Coder-Desktop/Coder-Desktop/UpdaterService.swift

Lines changed: 30 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -2,42 +2,59 @@ import Sparkle
22
import SwiftUI
33

44
final class UpdaterService: NSObject, ObservableObject {
5-
private lazy var inner: SPUStandardUpdaterController = .init(
6-
startingUpdater: true,
7-
updaterDelegate: self,
8-
userDriverDelegate: self
9-
)
10-
private var updater: SPUUpdater!
5+
// The auto-updater can be entirely disabled by setting the
6+
// `disableUpdater` UserDefaults key to `true`. This is designed for use in
7+
// MDM configurations, where the value can be set to `true` permanently.
8+
let disabled: Bool = UserDefaults.standard.bool(forKey: Keys.disableUpdater)
9+
1110
@Published var canCheckForUpdates = true
1211

1312
@Published var autoCheckForUpdates: Bool! {
1413
didSet {
1514
if let autoCheckForUpdates, autoCheckForUpdates != oldValue {
16-
updater.automaticallyChecksForUpdates = autoCheckForUpdates
15+
inner?.updater.automaticallyChecksForUpdates = autoCheckForUpdates
1716
}
1817
}
1918
}
2019

2120
@Published var updateChannel: UpdateChannel {
2221
didSet {
23-
UserDefaults.standard.set(updateChannel.rawValue, forKey: Self.updateChannelKey)
22+
UserDefaults.standard.set(updateChannel.rawValue, forKey: Keys.updateChannel)
2423
}
2524
}
2625

27-
static let updateChannelKey = "updateChannel"
26+
private var inner: (controller: SPUStandardUpdaterController, updater: SPUUpdater)?
2827

2928
override init() {
30-
updateChannel = UserDefaults.standard.string(forKey: Self.updateChannelKey)
29+
updateChannel = UserDefaults.standard.string(forKey: Keys.updateChannel)
3130
.flatMap { UpdateChannel(rawValue: $0) } ?? .stable
3231
super.init()
33-
updater = inner.updater
32+
33+
guard !disabled else {
34+
return
35+
}
36+
37+
let inner = SPUStandardUpdaterController(
38+
startingUpdater: true,
39+
updaterDelegate: self,
40+
userDriverDelegate: self
41+
)
42+
43+
let updater = inner.updater
44+
self.inner = (inner, updater)
45+
3446
autoCheckForUpdates = updater.automaticallyChecksForUpdates
3547
updater.publisher(for: \.canCheckForUpdates).assign(to: &$canCheckForUpdates)
3648
}
3749

3850
func checkForUpdates() {
39-
guard canCheckForUpdates else { return }
40-
updater.checkForUpdates()
51+
guard let inner, canCheckForUpdates else { return }
52+
inner.updater.checkForUpdates()
53+
}
54+
55+
enum Keys {
56+
static let disableUpdater = "disableUpdater"
57+
static let updateChannel = "updateChannel"
4158
}
4259
}
4360

Coder-Desktop/Coder-Desktop/VPN/VPNService.swift

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ final class CoderVPNService: NSObject, VPNService {
5656
var logger = Logger(subsystem: Bundle.main.bundleIdentifier!, category: "vpn")
5757
lazy var xpc: HelperXPCClient = .init(vpn: self)
5858

59-
@Published var tunnelState: VPNServiceState = .disabled {
59+
@Published private(set) var tunnelState: VPNServiceState = .disabled {
6060
didSet {
6161
if tunnelState == .connecting {
6262
progress = .init(stage: .initial, downloadProgress: nil)
@@ -80,9 +80,9 @@ final class CoderVPNService: NSObject, VPNService {
8080
return tunnelState
8181
}
8282

83-
@Published var progress: VPNProgress = .init(stage: .initial, downloadProgress: nil)
83+
@Published private(set) var progress: VPNProgress = .init(stage: .initial, downloadProgress: nil)
8484

85-
@Published var menuState: VPNMenuState = .init()
85+
@Published private(set) var menuState: VPNMenuState = .init()
8686

8787
// Whether the VPN should start as soon as possible
8888
var startWhenReady: Bool = false

Coder-Desktop/Coder-Desktop/Views/FileSync/FilePicker.swift

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -69,9 +69,9 @@ struct FilePicker: View {
6969

7070
@MainActor
7171
class FilePickerModel: ObservableObject {
72-
@Published var rootEntries: [FilePickerEntryModel] = []
73-
@Published var rootIsLoading: Bool = false
74-
@Published var error: SDKError?
72+
@Published private(set) var rootEntries: [FilePickerEntryModel] = []
73+
@Published private(set) var rootIsLoading: Bool = false
74+
@Published private(set) var error: SDKError?
7575

7676
// It's important that `AgentClient` is a reference type (class)
7777
// as we were having performance issues with a struct (unless it was a binding).
@@ -153,9 +153,9 @@ class FilePickerEntryModel: Identifiable, Hashable, ObservableObject {
153153

154154
let client: AgentClient
155155

156-
@Published var entries: [FilePickerEntryModel]?
157-
@Published var isLoading = false
158-
@Published var error: SDKError?
156+
@Published private(set) var entries: [FilePickerEntryModel]?
157+
@Published private(set) var isLoading = false
158+
@Published private(set) var error: SDKError?
159159
@Published private var innerIsExpanded = false
160160
var isExpanded: Bool {
161161
get { innerIsExpanded }

Coder-Desktop/Coder-Desktop/Views/Settings/GeneralTab.swift

Lines changed: 17 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -19,18 +19,25 @@ struct GeneralTab: View {
1919
Text("Start Coder Connect on launch")
2020
}
2121
}
22-
Section {
23-
Toggle(isOn: $updater.autoCheckForUpdates) {
24-
Text("Automatically check for updates")
25-
}
26-
Picker("Update channel", selection: $updater.updateChannel) {
27-
ForEach(UpdateChannel.allCases) { channel in
28-
Text(channel.name).tag(channel)
22+
if !updater.disabled {
23+
Section {
24+
Toggle(isOn: $updater.autoCheckForUpdates) {
25+
Text("Automatically check for updates")
26+
}
27+
Picker("Update channel", selection: $updater.updateChannel) {
28+
ForEach(UpdateChannel.allCases) { channel in
29+
Text(channel.name).tag(channel)
30+
}
31+
}
32+
HStack {
33+
Spacer()
34+
Button("Check for updates") { updater.checkForUpdates() }.disabled(!updater.canCheckForUpdates)
2935
}
3036
}
31-
HStack {
32-
Spacer()
33-
Button("Check for updates") { updater.checkForUpdates() }.disabled(!updater.canCheckForUpdates)
37+
} else {
38+
Section {
39+
Text("The app updater has been disabled by a device management policy.")
40+
.foregroundColor(.secondary)
3441
}
3542
}
3643
}.formStyle(.grouped)

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy