Merge pull request #569 from abiligiri/feature/managed_preferences

Disallow changes to managed preferences
This commit is contained in:
Matt Kiazyk 2024-10-15 21:38:49 -05:00 committed by GitHub
commit cf85e2fc5a
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
11 changed files with 70 additions and 21 deletions

View file

@ -13,8 +13,8 @@ extension AppState {
// check to see if we should auto install for the user // check to see if we should auto install for the user
public func autoInstallIfNeeded() { public func autoInstallIfNeeded() {
guard let storageValue = UserDefaults.standard.object(forKey: "autoInstallation") as? Int, let autoInstallType = AutoInstallationType(rawValue: storageValue) else { return } guard let storageValue = Current.defaults.get(forKey: "autoInstallation") as? Int, let autoInstallType = AutoInstallationType(rawValue: storageValue) else { return }
if autoInstallType == .none { return } if autoInstallType == .none { return }
// get newest xcode version // get newest xcode version

View file

@ -169,7 +169,7 @@ extension AppState {
// sets a proper cookie for runtimes // sets a proper cookie for runtimes
try await validateADCSession(path: downloadPath) try await validateADCSession(path: downloadPath)
let downloader = Downloader(rawValue: UserDefaults.standard.string(forKey: "downloader") ?? "aria2") ?? .aria2 let downloader = Downloader(rawValue: Current.defaults.string(forKey: "downloader") ?? "aria2") ?? .aria2
let url = URL(string: source)! let url = URL(string: source)!
let expectedRuntimePath = Path.xcodesApplicationSupport/"\(url.lastPathComponent)" let expectedRuntimePath = Path.xcodesApplicationSupport/"\(url.lastPathComponent)"

View file

@ -11,6 +11,23 @@ import DockProgress
import XcodesKit import XcodesKit
import LibFido2Swift import LibFido2Swift
enum PreferenceKey: String {
case installPath
case localPath
case unxipExperiment
case createSymLinkOnSelect
case onSelectActionType
case showOpenInRosettaOption
case autoInstallation
case SUEnableAutomaticChecks
case includePrereleaseVersions
case downloader
case dataSource
case xcodeListCategory
func isManaged() -> Bool { UserDefaults.standard.objectIsForced(forKey: self.rawValue) }
}
class AppState: ObservableObject { class AppState: ObservableObject {
private let client = AppleAPI.Client() private let client = AppleAPI.Client()
internal let runtimeService = RuntimeService() internal let runtimeService = RuntimeService()
@ -67,18 +84,24 @@ class AppState: ObservableObject {
} }
} }
var disableLocalPathChange: Bool { PreferenceKey.localPath.isManaged() }
@Published var installPath = "" { @Published var installPath = "" {
didSet { didSet {
Current.defaults.set(installPath, forKey: "installPath") Current.defaults.set(installPath, forKey: "installPath")
} }
} }
var disableInstallPathChange: Bool { PreferenceKey.installPath.isManaged() }
@Published var unxipExperiment = false { @Published var unxipExperiment = false {
didSet { didSet {
Current.defaults.set(unxipExperiment, forKey: "unxipExperiment") Current.defaults.set(unxipExperiment, forKey: "unxipExperiment")
} }
} }
var disableUnxipExperiment: Bool { PreferenceKey.unxipExperiment.isManaged() }
@Published var createSymLinkOnSelect = false { @Published var createSymLinkOnSelect = false {
didSet { didSet {
Current.defaults.set(createSymLinkOnSelect, forKey: "createSymLinkOnSelect") Current.defaults.set(createSymLinkOnSelect, forKey: "createSymLinkOnSelect")
@ -86,7 +109,7 @@ class AppState: ObservableObject {
} }
var createSymLinkOnSelectDisabled: Bool { var createSymLinkOnSelectDisabled: Bool {
return onSelectActionType == .rename return onSelectActionType == .rename || PreferenceKey.createSymLinkOnSelect.isManaged()
} }
@Published var onSelectActionType = SelectedActionType.none { @Published var onSelectActionType = SelectedActionType.none {
@ -99,6 +122,8 @@ class AppState: ObservableObject {
} }
} }
var onSelectActionTypeDisabled: Bool { PreferenceKey.onSelectActionType.isManaged() }
@Published var showOpenInRosettaOption = false { @Published var showOpenInRosettaOption = false {
didSet { didSet {
Current.defaults.set(showOpenInRosettaOption, forKey: "showOpenInRosettaOption") Current.defaults.set(showOpenInRosettaOption, forKey: "showOpenInRosettaOption")
@ -179,8 +204,8 @@ class AppState: ObservableObject {
// MARK: Timer // MARK: Timer
/// Runs a timer every 6 hours when app is open to check if it needs to auto install any xcodes /// Runs a timer every 6 hours when app is open to check if it needs to auto install any xcodes
func setupAutoInstallTimer() { func setupAutoInstallTimer() {
guard let storageValue = UserDefaults.standard.object(forKey: "autoInstallation") as? Int, let autoInstallType = AutoInstallationType(rawValue: storageValue) else { return } guard let storageValue = Current.defaults.get(forKey: "autoInstallation") as? Int, let autoInstallType = AutoInstallationType(rawValue: storageValue) else { return }
if autoInstallType == .none { return } if autoInstallType == .none { return }
autoInstallTimer = Timer.scheduledTimer(withTimeInterval: 60*60*6, repeats: true) { [weak self] _ in autoInstallTimer = Timer.scheduledTimer(withTimeInterval: 60*60*6, repeats: true) { [weak self] _ in
@ -546,7 +571,7 @@ class AppState: ObservableObject {
.mapError { $0 as Error } .mapError { $0 as Error }
} }
.flatMap { [unowned self] in .flatMap { [unowned self] in
self.install(.version(availableXcode), downloader: Downloader(rawValue: UserDefaults.standard.string(forKey: "downloader") ?? "aria2") ?? .aria2) self.install(.version(availableXcode), downloader: Downloader(rawValue: Current.defaults.string(forKey: "downloader") ?? "aria2") ?? .aria2)
} }
.receive(on: DispatchQueue.main) .receive(on: DispatchQueue.main)
.sink( .sink(
@ -572,7 +597,7 @@ class AppState: ObservableObject {
func installWithoutLogin(id: Xcode.ID) { func installWithoutLogin(id: Xcode.ID) {
guard let availableXcode = availableXcodes.first(where: { $0.version == id }) else { return } guard let availableXcode = availableXcodes.first(where: { $0.version == id }) else { return }
installationPublishers[id] = self.install(.version(availableXcode), downloader: Downloader(rawValue: UserDefaults.standard.string(forKey: "downloader") ?? "aria2") ?? .aria2) installationPublishers[id] = self.install(.version(availableXcode), downloader: Downloader(rawValue: Current.defaults.string(forKey: "downloader") ?? "aria2") ?? .aria2)
.receive(on: DispatchQueue.main) .receive(on: DispatchQueue.main)
.sink( .sink(
receiveCompletion: { [unowned self] completion in receiveCompletion: { [unowned self] completion in

View file

@ -14,4 +14,6 @@ public enum DataSource: String, CaseIterable, Identifiable, CustomStringConverti
case .xcodeReleases: return "Xcode Releases" case .xcodeReleases: return "Xcode Releases"
} }
} }
var isManaged: Bool { PreferenceKey.dataSource.isManaged() }
} }

View file

@ -13,4 +13,6 @@ public enum Downloader: String, CaseIterable, Identifiable, CustomStringConverti
case .aria2: return "aria2" case .aria2: return "aria2"
} }
} }
var isManaged: Bool { PreferenceKey.downloader.isManaged() }
} }

View file

@ -36,6 +36,7 @@ struct AdvancedPreferencePane: View {
self.appState.installPath = path.string self.appState.installPath = path.string
} }
} }
.disabled(appState.disableInstallPathChange)
Text("InstallPathDescription") Text("InstallPathDescription")
.font(.footnote) .font(.footnote)
.foregroundStyle(.secondary) .foregroundStyle(.secondary)
@ -72,6 +73,7 @@ struct AdvancedPreferencePane: View {
self.appState.localPath = path.string self.appState.localPath = path.string
} }
} }
.disabled(appState.disableLocalPathChange)
Text("LocalCachePathDescription") Text("LocalCachePathDescription")
.font(.footnote) .font(.footnote)
.foregroundStyle(.secondary) .foregroundStyle(.secondary)
@ -93,7 +95,8 @@ struct AdvancedPreferencePane: View {
} }
.labelsHidden() .labelsHidden()
.pickerStyle(.inline) .pickerStyle(.inline)
.disabled(appState.onSelectActionTypeDisabled)
Text(appState.onSelectActionType.detailedDescription) Text(appState.onSelectActionType.detailedDescription)
.font(.footnote) .font(.footnote)
.foregroundStyle(.secondary) .foregroundStyle(.secondary)

View file

@ -19,7 +19,7 @@ struct DownloadPreferencePane: View {
} }
.labelsHidden() .labelsHidden()
.fixedSize() .fixedSize()
Text("DataSourceDescription") Text("DataSourceDescription")
.font(.footnote) .font(.footnote)
.foregroundStyle(.secondary) .foregroundStyle(.secondary)
@ -27,7 +27,8 @@ struct DownloadPreferencePane: View {
} }
} }
.groupBoxStyle(PreferencesGroupBoxStyle()) .groupBoxStyle(PreferencesGroupBoxStyle())
.disabled(dataSource.isManaged)
GroupBox(label: Text("Downloader")) { GroupBox(label: Text("Downloader")) {
VStack(alignment: .leading) { VStack(alignment: .leading) {
Picker("Downloader", selection: $downloader) { Picker("Downloader", selection: $downloader) {
@ -38,7 +39,7 @@ struct DownloadPreferencePane: View {
} }
.labelsHidden() .labelsHidden()
.fixedSize() .fixedSize()
Text("DownloaderDescription") Text("DownloaderDescription")
.font(.footnote) .font(.footnote)
.foregroundStyle(.secondary) .foregroundStyle(.secondary)
@ -46,6 +47,7 @@ struct DownloadPreferencePane: View {
} }
} }
.groupBoxStyle(PreferencesGroupBoxStyle()) .groupBoxStyle(PreferencesGroupBoxStyle())
.disabled(downloader.isManaged)
} }
} }
} }

View file

@ -13,6 +13,7 @@ struct ExperimentsPreferencePane: View {
"UseUnxipExperiment", "UseUnxipExperiment",
isOn: $appState.unxipExperiment isOn: $appState.unxipExperiment
) )
.disabled(appState.disableUnxipExperiment)
Text("FasterUnxipDescription") Text("FasterUnxipDescription")
.font(.footnote) .font(.footnote)
.foregroundStyle(.secondary) .foregroundStyle(.secondary)

View file

@ -15,11 +15,13 @@ struct UpdatesPreferencePane: View {
"AutomaticInstallNewVersion", "AutomaticInstallNewVersion",
isOn: $autoInstallationType.isAutoInstalling isOn: $autoInstallationType.isAutoInstalling
) )
.disabled(updater.disableAutoInstallNewVersions)
Toggle( Toggle(
"IncludePreRelease", "IncludePreRelease",
isOn: $autoInstallationType.isAutoInstallingBeta isOn: $autoInstallationType.isAutoInstallingBeta
) )
.disabled(updater.disableIncludePrereleaseVersions)
} }
.fixedSize(horizontal: false, vertical: true) .fixedSize(horizontal: false, vertical: true)
} }
@ -34,17 +36,20 @@ struct UpdatesPreferencePane: View {
isOn: $updater.automaticallyChecksForUpdates isOn: $updater.automaticallyChecksForUpdates
) )
.fixedSize(horizontal: true, vertical: false) .fixedSize(horizontal: true, vertical: false)
.disabled(updater.disableAutoUpdateXcodesApp)
Toggle( Toggle(
"IncludePreRelease", "IncludePreRelease",
isOn: $updater.includePrereleaseVersions isOn: $updater.includePrereleaseVersions
) )
.disabled(updater.disableAutoUpdateXcodesAppPrereleaseVersions)
Button("CheckNow") { Button("CheckNow") {
updater.checkForUpdates() updater.checkForUpdates()
} }
.padding(.top) .padding(.top)
.disabled(updater.disableAutoUpdateXcodesApp)
Text(String(format: localizeString("LastChecked"), lastUpdatedString)) Text(String(format: localizeString("LastChecked"), lastUpdatedString))
.font(.footnote) .font(.footnote)
.foregroundStyle(.secondary) .foregroundStyle(.secondary)
@ -83,12 +88,18 @@ class ObservableUpdater: ObservableObject {
private var lastUpdateCheckDateObservation: NSKeyValueObservation? private var lastUpdateCheckDateObservation: NSKeyValueObservation?
@Published var includePrereleaseVersions = false { @Published var includePrereleaseVersions = false {
didSet { didSet {
UserDefaults.standard.setValue(includePrereleaseVersions, forKey: "includePrereleaseVersions") Current.defaults.set(includePrereleaseVersions, forKey: "includePrereleaseVersions")
updaterDelegate.includePrereleaseVersions = includePrereleaseVersions updaterDelegate.includePrereleaseVersions = includePrereleaseVersions
} }
} }
var disableAutoInstallNewVersions: Bool { PreferenceKey.autoInstallation.isManaged() }
var disableIncludePrereleaseVersions: Bool { PreferenceKey.autoInstallation.isManaged() }
var disableAutoUpdateXcodesApp: Bool { PreferenceKey.SUEnableAutomaticChecks.isManaged() }
var disableAutoUpdateXcodesAppPrereleaseVersions: Bool { PreferenceKey.includePrereleaseVersions.isManaged() }
init() { init() {
updater = SPUStandardUpdaterController(startingUpdater: true, updaterDelegate: updaterDelegate, userDriverDelegate: nil).updater updater = SPUStandardUpdaterController(startingUpdater: true, updaterDelegate: updaterDelegate, userDriverDelegate: nil).updater
@ -111,7 +122,7 @@ class ObservableUpdater: ObservableObject {
self.lastUpdateCheckDate = updater.lastUpdateCheckDate self.lastUpdateCheckDate = updater.lastUpdateCheckDate
} }
) )
includePrereleaseVersions = UserDefaults.standard.bool(forKey: "includePrereleaseVersions") includePrereleaseVersions = Current.defaults.bool(forKey: "includePrereleaseVersions") ?? false
} }
func checkForUpdates() { func checkForUpdates() {

View file

@ -44,6 +44,7 @@ struct MainToolbarModifier: ViewModifier {
} }
} }
.help("FilterAvailableDescription") .help("FilterAvailableDescription")
.disabled(category.isManaged)
Button(action: { Button(action: {
isInstalledOnly.toggle() isInstalledOnly.toggle()

View file

@ -14,4 +14,6 @@ enum XcodeListCategory: String, CaseIterable, Identifiable, CustomStringConverti
case .beta: return localizeString("Beta") case .beta: return localizeString("Beta")
} }
} }
var isManaged: Bool { PreferenceKey.xcodeListCategory.isManaged() }
} }