Auto Install Xcode if wanted

This commit is contained in:
Matt Kiazyk 2021-03-01 18:44:52 -06:00
parent e4165d5366
commit 8011f4a288
No known key found for this signature in database
GPG key ID: 33D9938D5D45EFE2
4 changed files with 119 additions and 23 deletions

View file

@ -8,6 +8,31 @@ import os.log
/// Downloads and installs Xcodes
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 }
if autoInstallType == .none { return }
// get newest xcode version
guard let newestXcode = allXcodes.first, newestXcode.installState == .notInstalled else {
Logger.appState.info("User has latest Xcode already installed")
return
}
if autoInstallType == .newestBeta {
Logger.appState.info("Auto installing newest Xcode Beta")
// install it, as user doesn't have it installed and it's either latest beta or latest release
install(id: newestXcode.id)
} else if autoInstallType == .newestVersion && newestXcode.version.isNotPrerelease {
Logger.appState.info("Auto installing newest Xcode")
install(id: newestXcode.id)
} else {
Logger.appState.info("No new Xcodes version found to auto install")
}
}
public func install(_ installationType: InstallationType, downloader: Downloader) -> AnyPublisher<Void, Error> {
install(installationType, downloader: downloader, attemptNumber: 0)
.map { _ in Void() }
@ -517,5 +542,30 @@ public enum InstallationType {
case version(AvailableXcode)
}
public enum AutoInstallationType: Int, Identifiable {
case none = 0
case newestVersion
case newestBeta
public var id: Self { self }
public var isAutoInstalling: Bool {
get {
return self != .none
}
set {
self = newValue ? .newestVersion : .none
}
}
public var isAutoInstallingBeta: Bool {
get {
return self == .newestBeta
}
set {
self = newValue ? .newestBeta : (isAutoInstalling ? .newestVersion : .none)
}
}
}
let XcodeTeamIdentifier = "59GAB85EFG"
let XcodeCertificateAuthority = ["Software Signing", "Apple Code Signing Certification Authority", "Apple Root CA"]

View file

@ -6,11 +6,20 @@ import SwiftSoup
import struct XCModel.Xcode
extension AppState {
var isReadyForUpdate: Bool {
guard let lastUpdated = Current.defaults.date(forKey: "lastUpdated"),
// This is bad date math but for this use case it doesn't need to be exact
lastUpdated < Current.date().addingTimeInterval(-60 * 60 * 5)
else {
return false
}
return true
}
func updateIfNeeded() {
guard
let lastUpdated = Current.defaults.date(forKey: "lastUpdated"),
// This is bad date math but for this use case it doesn't need to be exact
lastUpdated < Current.date().addingTimeInterval(-60 * 60 * 24)
isReadyForUpdate
else {
updatePublisher = updateSelectedXcodePath()
.sink(

View file

@ -22,6 +22,9 @@ class AppState: ObservableObject {
selectedXcodePath: selectedXcodePath
)
}
didSet {
autoInstallIfNeeded()
}
}
@Published var allXcodes: [Xcode] = []
@Published var selectedXcodePath: String? {
@ -56,7 +59,7 @@ class AppState: ObservableObject {
private var installationPublishers: [Version: AnyCancellable] = [:]
private var selectPublisher: AnyCancellable?
private var uninstallPublisher: AnyCancellable?
private var autoInstallTimer: Timer?
// MARK: -
var dataSource: DataSource {
@ -69,8 +72,20 @@ class AppState: ObservableObject {
guard !isTesting else { return }
try? loadCachedAvailableXcodes()
checkIfHelperIsInstalled()
setupAutoInstallTimer()
}
// 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 }
if autoInstallType == .none { return }
autoInstallTimer = Timer.scheduledTimer(withTimeInterval: 60*60*6, repeats: true) { [weak self] _ in
self?.updateIfNeeded()
}
}
// MARK: - Authentication
func validateSession() -> AnyPublisher<Void, Error> {

View file

@ -5,29 +5,51 @@ import SwiftUI
struct UpdatesPreferencePane: View {
@StateObject var updater = ObservableUpdater()
@AppStorage("autoInstallation") var autoInstallationType: AutoInstallationType = .none
var body: some View {
GroupBox(label: Text("Updates")) {
VStack(alignment: .leading) {
Toggle(
"Automatically check for app updates",
isOn: $updater.automaticallyChecksForUpdates
)
Toggle(
"Include prerelease app versions",
isOn: $updater.includePrereleaseVersions
)
Button("Check Now") {
SUUpdater.shared()?.checkForUpdates(nil)
VStack(alignment: .leading, spacing: 20) {
GroupBox(label: Text("Versions")) {
VStack(alignment: .leading) {
Toggle(
"Automatically install new versions of Xcode",
isOn: $autoInstallationType.isAutoInstalling
)
Toggle(
"Include prerelease/beta versions",
isOn: $autoInstallationType.isAutoInstallingBeta
)
}
Text("Last checked: \(lastUpdatedString)")
.font(.footnote)
.fixedSize(horizontal: false, vertical: true)
}
.frame(maxWidth: .infinity, alignment: .leading)
.groupBoxStyle(PreferencesGroupBoxStyle())
Divider()
GroupBox(label: Text("Xcodes.app Updates")) {
VStack(alignment: .leading) {
Toggle(
"Automatically check for app updates",
isOn: $updater.automaticallyChecksForUpdates
)
Toggle(
"Include prerelease app versions",
isOn: $updater.includePrereleaseVersions
)
Button("Check Now") {
SUUpdater.shared()?.checkForUpdates(nil)
}
Text("Last checked: \(lastUpdatedString)")
.font(.footnote)
}
.frame(maxWidth: .infinity, alignment: .leading)
}
.groupBoxStyle(PreferencesGroupBoxStyle())
}
.groupBoxStyle(PreferencesGroupBoxStyle())
.frame(width: 400)
}