diff --git a/Xcodes/Backend/AppState+Install.swift b/Xcodes/Backend/AppState+Install.swift index d9d294ef..2c9fc846 100644 --- a/Xcodes/Backend/AppState+Install.swift +++ b/Xcodes/Backend/AppState+Install.swift @@ -13,8 +13,8 @@ extension AppState { // check to see if we should auto install for the user 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 } // get newest xcode version diff --git a/Xcodes/Backend/AppState+Runtimes.swift b/Xcodes/Backend/AppState+Runtimes.swift index ca8f5e46..33953ae8 100644 --- a/Xcodes/Backend/AppState+Runtimes.swift +++ b/Xcodes/Backend/AppState+Runtimes.swift @@ -169,7 +169,7 @@ extension AppState { // sets a proper cookie for runtimes 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 expectedRuntimePath = Path.xcodesApplicationSupport/"\(url.lastPathComponent)" diff --git a/Xcodes/Backend/AppState.swift b/Xcodes/Backend/AppState.swift index 11906294..10aeb155 100644 --- a/Xcodes/Backend/AppState.swift +++ b/Xcodes/Backend/AppState.swift @@ -11,6 +11,23 @@ import DockProgress import XcodesKit 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 { private let client = AppleAPI.Client() internal let runtimeService = RuntimeService() @@ -67,18 +84,24 @@ class AppState: ObservableObject { } } + var disableLocalPathChange: Bool { PreferenceKey.localPath.isManaged() } + @Published var installPath = "" { didSet { Current.defaults.set(installPath, forKey: "installPath") } } - + + var disableInstallPathChange: Bool { PreferenceKey.installPath.isManaged() } + @Published var unxipExperiment = false { didSet { Current.defaults.set(unxipExperiment, forKey: "unxipExperiment") } } + var disableUnxipExperiment: Bool { PreferenceKey.unxipExperiment.isManaged() } + @Published var createSymLinkOnSelect = false { didSet { Current.defaults.set(createSymLinkOnSelect, forKey: "createSymLinkOnSelect") @@ -86,7 +109,7 @@ class AppState: ObservableObject { } var createSymLinkOnSelectDisabled: Bool { - return onSelectActionType == .rename + return onSelectActionType == .rename || PreferenceKey.createSymLinkOnSelect.isManaged() } @Published var onSelectActionType = SelectedActionType.none { @@ -99,6 +122,8 @@ class AppState: ObservableObject { } } + var onSelectActionTypeDisabled: Bool { PreferenceKey.onSelectActionType.isManaged() } + @Published var showOpenInRosettaOption = false { didSet { Current.defaults.set(showOpenInRosettaOption, forKey: "showOpenInRosettaOption") @@ -179,8 +204,8 @@ class AppState: ObservableObject { // MARK: Timer /// Runs a timer every 6 hours when app is open to check if it needs to auto install any xcodes 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 } autoInstallTimer = Timer.scheduledTimer(withTimeInterval: 60*60*6, repeats: true) { [weak self] _ in @@ -546,7 +571,7 @@ class AppState: ObservableObject { .mapError { $0 as Error } } .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) .sink( @@ -572,7 +597,7 @@ class AppState: ObservableObject { func installWithoutLogin(id: Xcode.ID) { 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) .sink( receiveCompletion: { [unowned self] completion in diff --git a/Xcodes/Backend/DataSource.swift b/Xcodes/Backend/DataSource.swift index 69363ae3..03911848 100644 --- a/Xcodes/Backend/DataSource.swift +++ b/Xcodes/Backend/DataSource.swift @@ -14,4 +14,6 @@ public enum DataSource: String, CaseIterable, Identifiable, CustomStringConverti case .xcodeReleases: return "Xcode Releases" } } + + var isManaged: Bool { PreferenceKey.dataSource.isManaged() } } diff --git a/Xcodes/Backend/Downloader.swift b/Xcodes/Backend/Downloader.swift index 41eb6807..e155703a 100644 --- a/Xcodes/Backend/Downloader.swift +++ b/Xcodes/Backend/Downloader.swift @@ -13,4 +13,6 @@ public enum Downloader: String, CaseIterable, Identifiable, CustomStringConverti case .aria2: return "aria2" } } + + var isManaged: Bool { PreferenceKey.downloader.isManaged() } } diff --git a/Xcodes/Frontend/Preferences/AdvancedPreferencePane.swift b/Xcodes/Frontend/Preferences/AdvancedPreferencePane.swift index c9531c5c..13ab718e 100644 --- a/Xcodes/Frontend/Preferences/AdvancedPreferencePane.swift +++ b/Xcodes/Frontend/Preferences/AdvancedPreferencePane.swift @@ -36,6 +36,7 @@ struct AdvancedPreferencePane: View { self.appState.installPath = path.string } } + .disabled(appState.disableInstallPathChange) Text("InstallPathDescription") .font(.footnote) .foregroundStyle(.secondary) @@ -72,6 +73,7 @@ struct AdvancedPreferencePane: View { self.appState.localPath = path.string } } + .disabled(appState.disableLocalPathChange) Text("LocalCachePathDescription") .font(.footnote) .foregroundStyle(.secondary) @@ -93,7 +95,8 @@ struct AdvancedPreferencePane: View { } .labelsHidden() .pickerStyle(.inline) - + .disabled(appState.onSelectActionTypeDisabled) + Text(appState.onSelectActionType.detailedDescription) .font(.footnote) .foregroundStyle(.secondary) diff --git a/Xcodes/Frontend/Preferences/DownloadPreferencePane.swift b/Xcodes/Frontend/Preferences/DownloadPreferencePane.swift index 62adf539..854c6159 100644 --- a/Xcodes/Frontend/Preferences/DownloadPreferencePane.swift +++ b/Xcodes/Frontend/Preferences/DownloadPreferencePane.swift @@ -19,7 +19,7 @@ struct DownloadPreferencePane: View { } .labelsHidden() .fixedSize() - + Text("DataSourceDescription") .font(.footnote) .foregroundStyle(.secondary) @@ -27,7 +27,8 @@ struct DownloadPreferencePane: View { } } .groupBoxStyle(PreferencesGroupBoxStyle()) - + .disabled(dataSource.isManaged) + GroupBox(label: Text("Downloader")) { VStack(alignment: .leading) { Picker("Downloader", selection: $downloader) { @@ -38,7 +39,7 @@ struct DownloadPreferencePane: View { } .labelsHidden() .fixedSize() - + Text("DownloaderDescription") .font(.footnote) .foregroundStyle(.secondary) @@ -46,6 +47,7 @@ struct DownloadPreferencePane: View { } } .groupBoxStyle(PreferencesGroupBoxStyle()) + .disabled(downloader.isManaged) } } } diff --git a/Xcodes/Frontend/Preferences/ExperiementsPreferencePane.swift b/Xcodes/Frontend/Preferences/ExperiementsPreferencePane.swift index 5633f8f0..68dad51e 100644 --- a/Xcodes/Frontend/Preferences/ExperiementsPreferencePane.swift +++ b/Xcodes/Frontend/Preferences/ExperiementsPreferencePane.swift @@ -13,6 +13,7 @@ struct ExperimentsPreferencePane: View { "UseUnxipExperiment", isOn: $appState.unxipExperiment ) + .disabled(appState.disableUnxipExperiment) Text("FasterUnxipDescription") .font(.footnote) .foregroundStyle(.secondary) diff --git a/Xcodes/Frontend/Preferences/UpdatesPreferencePane.swift b/Xcodes/Frontend/Preferences/UpdatesPreferencePane.swift index 2f52418a..edad69bd 100644 --- a/Xcodes/Frontend/Preferences/UpdatesPreferencePane.swift +++ b/Xcodes/Frontend/Preferences/UpdatesPreferencePane.swift @@ -15,11 +15,13 @@ struct UpdatesPreferencePane: View { "AutomaticInstallNewVersion", isOn: $autoInstallationType.isAutoInstalling ) - + .disabled(updater.disableAutoInstallNewVersions) + Toggle( "IncludePreRelease", isOn: $autoInstallationType.isAutoInstallingBeta ) + .disabled(updater.disableIncludePrereleaseVersions) } .fixedSize(horizontal: false, vertical: true) } @@ -34,17 +36,20 @@ struct UpdatesPreferencePane: View { isOn: $updater.automaticallyChecksForUpdates ) .fixedSize(horizontal: true, vertical: false) - + .disabled(updater.disableAutoUpdateXcodesApp) + Toggle( "IncludePreRelease", isOn: $updater.includePrereleaseVersions ) - + .disabled(updater.disableAutoUpdateXcodesAppPrereleaseVersions) + Button("CheckNow") { updater.checkForUpdates() } .padding(.top) - + .disabled(updater.disableAutoUpdateXcodesApp) + Text(String(format: localizeString("LastChecked"), lastUpdatedString)) .font(.footnote) .foregroundStyle(.secondary) @@ -83,12 +88,18 @@ class ObservableUpdater: ObservableObject { private var lastUpdateCheckDateObservation: NSKeyValueObservation? @Published var includePrereleaseVersions = false { didSet { - UserDefaults.standard.setValue(includePrereleaseVersions, forKey: "includePrereleaseVersions") - + Current.defaults.set(includePrereleaseVersions, forKey: "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() { updater = SPUStandardUpdaterController(startingUpdater: true, updaterDelegate: updaterDelegate, userDriverDelegate: nil).updater @@ -111,7 +122,7 @@ class ObservableUpdater: ObservableObject { self.lastUpdateCheckDate = updater.lastUpdateCheckDate } ) - includePrereleaseVersions = UserDefaults.standard.bool(forKey: "includePrereleaseVersions") + includePrereleaseVersions = Current.defaults.bool(forKey: "includePrereleaseVersions") ?? false } func checkForUpdates() { diff --git a/Xcodes/Frontend/XcodeList/MainToolbar.swift b/Xcodes/Frontend/XcodeList/MainToolbar.swift index 24b5193d..4ba2967a 100644 --- a/Xcodes/Frontend/XcodeList/MainToolbar.swift +++ b/Xcodes/Frontend/XcodeList/MainToolbar.swift @@ -44,6 +44,7 @@ struct MainToolbarModifier: ViewModifier { } } .help("FilterAvailableDescription") + .disabled(category.isManaged) Button(action: { isInstalledOnly.toggle() diff --git a/Xcodes/Frontend/XcodeList/XcodeListCategory.swift b/Xcodes/Frontend/XcodeList/XcodeListCategory.swift index 52ea7905..328f6364 100644 --- a/Xcodes/Frontend/XcodeList/XcodeListCategory.swift +++ b/Xcodes/Frontend/XcodeList/XcodeListCategory.swift @@ -14,4 +14,6 @@ enum XcodeListCategory: String, CaseIterable, Identifiable, CustomStringConverti case .beta: return localizeString("Beta") } } + + var isManaged: Bool { PreferenceKey.xcodeListCategory.isManaged() } }