mirror of
https://github.com/XcodesOrg/XcodesApp.git
synced 2026-04-26 14:57:37 +00:00
Merge pull request #569 from abiligiri/feature/managed_preferences
Disallow changes to managed preferences
This commit is contained in:
commit
cf85e2fc5a
11 changed files with 70 additions and 21 deletions
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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)"
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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() }
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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() }
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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)
|
||||||
|
|
|
||||||
|
|
@ -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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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)
|
||||||
|
|
|
||||||
|
|
@ -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() {
|
||||||
|
|
|
||||||
|
|
@ -44,6 +44,7 @@ struct MainToolbarModifier: ViewModifier {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.help("FilterAvailableDescription")
|
.help("FilterAvailableDescription")
|
||||||
|
.disabled(category.isManaged)
|
||||||
|
|
||||||
Button(action: {
|
Button(action: {
|
||||||
isInstalledOnly.toggle()
|
isInstalledOnly.toggle()
|
||||||
|
|
|
||||||
|
|
@ -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() }
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue