mirror of
https://github.com/XcodesOrg/XcodesApp.git
synced 2026-03-25 08:55:46 +00:00
add option to rename Xcode to Xcode.app on select.
This commit is contained in:
parent
491e810a3c
commit
b64af2fc44
7 changed files with 133 additions and 9 deletions
|
|
@ -108,6 +108,7 @@
|
|||
E8CBDB8927ADE32300B22292 /* unxip in Copy aria2c */ = {isa = PBXBuildFile; fileRef = E8CBDB8627ADD92000B22292 /* unxip */; settings = {ATTRIBUTES = (CodeSignOnCopy, ); }; };
|
||||
E8CBDB8B27AE02FF00B22292 /* ExperiementsPreferencePane.swift in Sources */ = {isa = PBXBuildFile; fileRef = E8CBDB8A27AE02FF00B22292 /* ExperiementsPreferencePane.swift */; };
|
||||
E8D0296F284B029800647641 /* BottomStatusBar.swift in Sources */ = {isa = PBXBuildFile; fileRef = E8D0296E284B029800647641 /* BottomStatusBar.swift */; };
|
||||
E8D655C0288DD04700A139C2 /* SelectedActionType.swift in Sources */ = {isa = PBXBuildFile; fileRef = E8D655BF288DD04700A139C2 /* SelectedActionType.swift */; };
|
||||
E8DA461125FAF7FB002E85EF /* NotificationsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = E8DA461025FAF7FB002E85EF /* NotificationsView.swift */; };
|
||||
E8E98A9025D8631800EC89A0 /* InstallationStepRowView.swift in Sources */ = {isa = PBXBuildFile; fileRef = CAFBC3FF259AC17F00E2A3D8 /* InstallationStepRowView.swift */; };
|
||||
E8E98A9625D863D700EC89A0 /* InstallationStepDetailView.swift in Sources */ = {isa = PBXBuildFile; fileRef = E8E98A9525D863D700EC89A0 /* InstallationStepDetailView.swift */; };
|
||||
|
|
@ -296,6 +297,7 @@
|
|||
E8CBDB8627ADD92000B22292 /* unxip */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.executable"; path = unxip; sourceTree = "<group>"; };
|
||||
E8CBDB8A27AE02FF00B22292 /* ExperiementsPreferencePane.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ExperiementsPreferencePane.swift; sourceTree = "<group>"; };
|
||||
E8D0296E284B029800647641 /* BottomStatusBar.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BottomStatusBar.swift; sourceTree = "<group>"; };
|
||||
E8D655BF288DD04700A139C2 /* SelectedActionType.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SelectedActionType.swift; sourceTree = "<group>"; };
|
||||
E8DA461025FAF7FB002E85EF /* NotificationsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotificationsView.swift; sourceTree = "<group>"; };
|
||||
E8E98A9525D863D700EC89A0 /* InstallationStepDetailView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InstallationStepDetailView.swift; sourceTree = "<group>"; };
|
||||
/* End PBXFileReference section */
|
||||
|
|
@ -479,6 +481,7 @@
|
|||
CA11E7B92598476C00D2EE1C /* XcodeCommands.swift */,
|
||||
E87DD6EA25D053FA00D86808 /* Progress+.swift */,
|
||||
E81D7E9F2805250100A205FC /* Collection+.swift */,
|
||||
E8D655BF288DD04700A139C2 /* SelectedActionType.swift */,
|
||||
);
|
||||
path = Backend;
|
||||
sourceTree = "<group>";
|
||||
|
|
@ -844,6 +847,7 @@
|
|||
CAFE4AB425B7D3AF0064FE51 /* AdvancedPreferencePane.swift in Sources */,
|
||||
CA9FF84E2595079F00E47BAF /* ScrollingTextView.swift in Sources */,
|
||||
CABFA9C12592EEEA00380FEE /* Version+.swift in Sources */,
|
||||
E8D655C0288DD04700A139C2 /* SelectedActionType.swift in Sources */,
|
||||
CA9FF8522595080100E47BAF /* AcknowledgementsView.swift in Sources */,
|
||||
CABFA9CE2592EEEA00380FEE /* Version+Xcode.swift in Sources */,
|
||||
CAFBDB912598FE80003DCC5A /* SelectedXcode.swift in Sources */,
|
||||
|
|
|
|||
|
|
@ -74,6 +74,20 @@ class AppState: ObservableObject {
|
|||
}
|
||||
}
|
||||
|
||||
var createSymLinkOnSelectDisabled: Bool {
|
||||
return onSelectActionType == .rename
|
||||
}
|
||||
|
||||
@Published var onSelectActionType = SelectedActionType.none {
|
||||
didSet {
|
||||
Current.defaults.set(onSelectActionType.rawValue, forKey: "onSelectActionType")
|
||||
|
||||
if onSelectActionType == .rename {
|
||||
createSymLinkOnSelect = false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Publisher Cancellables
|
||||
|
||||
var cancellables = Set<AnyCancellable>()
|
||||
|
|
@ -120,6 +134,7 @@ class AppState: ObservableObject {
|
|||
localPath = Current.defaults.string(forKey: "localPath") ?? Path.defaultXcodesApplicationSupport.string
|
||||
unxipExperiment = Current.defaults.bool(forKey: "unxipExperiment") ?? false
|
||||
createSymLinkOnSelect = Current.defaults.bool(forKey: "createSymLinkOnSelect") ?? false
|
||||
onSelectActionType = SelectedActionType(rawValue: Current.defaults.string(forKey: "onSelectActionType") ?? "none") ?? .none
|
||||
}
|
||||
|
||||
// MARK: Timer
|
||||
|
|
@ -492,10 +507,15 @@ class AppState: ObservableObject {
|
|||
}
|
||||
|
||||
guard
|
||||
let installedXcodePath = xcode.installedPath,
|
||||
var installedXcodePath = xcode.installedPath,
|
||||
selectPublisher == nil
|
||||
else { return }
|
||||
|
||||
if onSelectActionType == .rename {
|
||||
guard let newDestinationXcodePath = renameToXcode(xcode: xcode) else { return }
|
||||
installedXcodePath = newDestinationXcodePath
|
||||
}
|
||||
|
||||
selectPublisher = installHelperIfNecessary()
|
||||
.flatMap {
|
||||
Current.helper.switchXcodePath(installedXcodePath.string)
|
||||
|
|
@ -575,7 +595,39 @@ class AppState: ObservableObject {
|
|||
self.error = error
|
||||
self.presentedAlert = .generic(title: localizeString("Alert.SymLink.Title"), message: error.legibleLocalizedDescription)
|
||||
}
|
||||
}
|
||||
|
||||
func renameToXcode(xcode: Xcode) -> Path? {
|
||||
guard let installedXcodePath = xcode.installedPath else { return nil }
|
||||
|
||||
let destinationPath: Path = Path.installDirectory/"Xcode.app"
|
||||
|
||||
// rename any old named `Xcode.app` to the Xcodes versioned named files
|
||||
if FileManager.default.fileExists(atPath: destinationPath.string) {
|
||||
if let originalXcode = Current.files.installedXcode(destination: destinationPath) {
|
||||
let newName = "Xcode-\(originalXcode.version.descriptionWithoutBuildMetadata).app"
|
||||
Logger.appState.debug("Found Xcode.app - renaming back to \(newName)")
|
||||
do {
|
||||
try destinationPath.rename(to: newName)
|
||||
} catch {
|
||||
Logger.appState.error("Unable to create rename Xcode.app back to original")
|
||||
self.error = error
|
||||
// TODO UPDATE MY ERROR STRING
|
||||
self.presentedAlert = .generic(title: localizeString("Alert.SymLink.Title"), message: error.legibleLocalizedDescription)
|
||||
}
|
||||
}
|
||||
}
|
||||
// rename passed in xcode to xcode.app
|
||||
Logger.appState.debug("Found Xcode.app - renaming back to Xcode.app")
|
||||
do {
|
||||
return try installedXcodePath.rename(to: "Xcode.app")
|
||||
} catch {
|
||||
Logger.appState.error("Unable to create rename Xcode.app back to original")
|
||||
self.error = error
|
||||
// TODO UPDATE MY ERROR STRING
|
||||
self.presentedAlert = .generic(title: localizeString("Alert.SymLink.Title"), message: error.legibleLocalizedDescription)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func updateAllXcodes(availableXcodes: [AvailableXcode], installedXcodes: [InstalledXcode], selectedXcodePath: String?) {
|
||||
|
|
|
|||
|
|
@ -2,13 +2,12 @@ import Foundation
|
|||
import Path
|
||||
|
||||
extension Entry {
|
||||
var isAppBundle: Bool {
|
||||
static func isAppBundle(kind: Kind, path: Path) -> Bool {
|
||||
kind == .directory &&
|
||||
path.extension == "app" &&
|
||||
!path.isSymlink
|
||||
}
|
||||
|
||||
var infoPlist: InfoPlist? {
|
||||
static func infoPlist(kind: Kind, path: Path) -> InfoPlist? {
|
||||
let infoPlistPath = path.join("Contents").join("Info.plist")
|
||||
guard
|
||||
let infoPlistData = try? Data(contentsOf: infoPlistPath.url),
|
||||
|
|
@ -17,4 +16,12 @@ extension Entry {
|
|||
|
||||
return infoPlist
|
||||
}
|
||||
|
||||
var isAppBundle: Bool {
|
||||
Entry.isAppBundle(kind: kind, path: path)
|
||||
}
|
||||
|
||||
var infoPlist: InfoPlist? {
|
||||
Entry.infoPlist(kind: kind, path: path)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -164,7 +164,16 @@ public struct Files {
|
|||
}
|
||||
|
||||
public var installedXcodes = _installedXcodes
|
||||
|
||||
public func installedXcode(destination: Path) -> InstalledXcode? {
|
||||
if Entry.isAppBundle(kind: destination.isDirectory ? .directory : .file, path: destination) && Entry.infoPlist(kind: destination.isDirectory ? .directory : .file, path: destination)?.bundleID == "com.apple.dt.Xcode" {
|
||||
return InstalledXcode.init(path: destination)
|
||||
} else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private func _installedXcodes(destination: Path) -> [InstalledXcode] {
|
||||
((try? destination.ls()) ?? [])
|
||||
.filter { $0.isAppBundle && $0.infoPlist?.bundleID == "com.apple.dt.Xcode" }
|
||||
|
|
|
|||
31
Xcodes/Backend/SelectedActionType.swift
Normal file
31
Xcodes/Backend/SelectedActionType.swift
Normal file
|
|
@ -0,0 +1,31 @@
|
|||
//
|
||||
// SelectedActionType.swift
|
||||
// Xcodes
|
||||
//
|
||||
// Created by Matt Kiazyk on 2022-07-24.
|
||||
// Copyright © 2022 Robots and Pencils. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
public enum SelectedActionType: String, CaseIterable, Identifiable, CustomStringConvertible {
|
||||
case none
|
||||
case rename
|
||||
|
||||
public var id: Self { self }
|
||||
|
||||
public static var `default` = SelectedActionType.none
|
||||
|
||||
public var description: String {
|
||||
switch self {
|
||||
case .none: return localizeString("OnSelectDoNothing")
|
||||
case .rename: return localizeString("OnSelectRenameXcode")
|
||||
}
|
||||
}
|
||||
|
||||
public var detailedDescription: String {
|
||||
switch self {
|
||||
case .none: return localizeString("OnSelectDoNothingDescription")
|
||||
case .rename: return localizeString("OnSelectRenameXcodeDescription")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -48,13 +48,27 @@ struct AdvancedPreferencePane: View {
|
|||
|
||||
GroupBox(label: Text("Active/Select")) {
|
||||
VStack(alignment: .leading) {
|
||||
Toggle(
|
||||
"AutomaticallyCreateSymbolicLink",
|
||||
isOn: $appState.createSymLinkOnSelect
|
||||
)
|
||||
Text("AutomaticallyCreateSymbolicLinkDescription")
|
||||
Picker("OnSelect", selection: $appState.onSelectActionType) {
|
||||
|
||||
Text(SelectedActionType.none.description)
|
||||
.tag(SelectedActionType.none)
|
||||
Text(SelectedActionType.rename.description)
|
||||
.tag(SelectedActionType.rename)
|
||||
}
|
||||
.labelsHidden()
|
||||
.pickerStyle(.inline)
|
||||
|
||||
Text(appState.onSelectActionType.detailedDescription)
|
||||
.font(.footnote)
|
||||
.fixedSize(horizontal: false, vertical: true)
|
||||
Spacer()
|
||||
.frame(height: 20)
|
||||
|
||||
Toggle("AutomaticallyCreateSymbolicLink", isOn: $appState.createSymLinkOnSelect)
|
||||
.disabled(appState.createSymLinkOnSelectDisabled)
|
||||
Text("AutomaticallyCreateSymbolicLinkDescription")
|
||||
.font(.footnote)
|
||||
.fixedSize(horizontal: false, vertical: true)
|
||||
}
|
||||
.fixedSize(horizontal: false, vertical: true)
|
||||
}
|
||||
|
|
@ -154,6 +168,7 @@ struct AdvancedPreferencePane_Previews: PreviewProvider {
|
|||
AdvancedPreferencePane()
|
||||
.environmentObject(AppState())
|
||||
}
|
||||
.frame(width: 500, height: 700, alignment: .center)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -78,8 +78,14 @@
|
|||
"LocalCachePathDescription" = "Xcodes caches available Xcode versions and temporary downloads new versions to a directory";
|
||||
"Change" = "Change";
|
||||
"Active/Select" = "Active/Select";
|
||||
|
||||
"OnSelectDoNothing" = "Keep name as Xcode-X.X.X.app";
|
||||
"OnSelectDoNothingDescription" = "On select, will keep the name as the version eg. Xcode-13.4.1.app";
|
||||
"AutomaticallyCreateSymbolicLink" = "Automatically create symbolic link to Xcode.app";
|
||||
"AutomaticallyCreateSymbolicLinkDescription" = "When making an Xcode version Active/Selected, try and create a symbolic link named Xcode.app in the installation directory";
|
||||
"OnSelectRenameXcode" = "Always rename to Xcode.app";
|
||||
"OnSelectRenameXcodeDescription" = "On select, will automatically try and rename the active Xcode to Xcode.app, renaming the previous Xcode.app to the version name.";
|
||||
|
||||
"DataSource" = "Data Source";
|
||||
"DataSourceDescription" = "The Apple data source scrapes the Apple Developer website. It will always show the latest releases that are available, but is more fragile.\n\nXcode Releases is an unofficial list of Xcode releases. It's provided as well-formed data, contains extra information that is not readily available from Apple, and is less likely to break if Apple redesigns their developer website.";
|
||||
"Downloader" = "Downloader";
|
||||
|
|
|
|||
Loading…
Reference in a new issue