mirror of
https://github.com/XcodesOrg/XcodesApp.git
synced 2026-04-24 14:37:38 +00:00
start moving over to XcodesKit package. Runtimes searching
This commit is contained in:
parent
d8e1069a92
commit
4f25905f4c
31 changed files with 749 additions and 54 deletions
|
|
@ -18,7 +18,6 @@
|
||||||
CA25192A25A9644800F08414 /* XcodeInstallState.swift in Sources */ = {isa = PBXBuildFile; fileRef = CA25192925A9644800F08414 /* XcodeInstallState.swift */; };
|
CA25192A25A9644800F08414 /* XcodeInstallState.swift in Sources */ = {isa = PBXBuildFile; fileRef = CA25192925A9644800F08414 /* XcodeInstallState.swift */; };
|
||||||
CA378F992466567600A58CE0 /* AppState.swift in Sources */ = {isa = PBXBuildFile; fileRef = CA378F982466567600A58CE0 /* AppState.swift */; };
|
CA378F992466567600A58CE0 /* AppState.swift in Sources */ = {isa = PBXBuildFile; fileRef = CA378F982466567600A58CE0 /* AppState.swift */; };
|
||||||
CA39711924495F0E00AFFB77 /* AppStoreButtonStyle.swift in Sources */ = {isa = PBXBuildFile; fileRef = CA39711824495F0E00AFFB77 /* AppStoreButtonStyle.swift */; };
|
CA39711924495F0E00AFFB77 /* AppStoreButtonStyle.swift in Sources */ = {isa = PBXBuildFile; fileRef = CA39711824495F0E00AFFB77 /* AppStoreButtonStyle.swift */; };
|
||||||
CA42DD6E25AEA8B200BC0B0C /* Logger.swift in Sources */ = {isa = PBXBuildFile; fileRef = CA42DD6D25AEA8B200BC0B0C /* Logger.swift */; };
|
|
||||||
CA42DD7325AEB04300BC0B0C /* Logger.swift in Sources */ = {isa = PBXBuildFile; fileRef = CA42DD7225AEB04300BC0B0C /* Logger.swift */; };
|
CA42DD7325AEB04300BC0B0C /* Logger.swift in Sources */ = {isa = PBXBuildFile; fileRef = CA42DD7225AEB04300BC0B0C /* Logger.swift */; };
|
||||||
CA44901F2463AD34003D8213 /* Tag.swift in Sources */ = {isa = PBXBuildFile; fileRef = CA44901E2463AD34003D8213 /* Tag.swift */; };
|
CA44901F2463AD34003D8213 /* Tag.swift in Sources */ = {isa = PBXBuildFile; fileRef = CA44901E2463AD34003D8213 /* Tag.swift */; };
|
||||||
CA452BB0259FD9770072DFA4 /* ProgressIndicator.swift in Sources */ = {isa = PBXBuildFile; fileRef = CA452BAF259FD9770072DFA4 /* ProgressIndicator.swift */; };
|
CA452BB0259FD9770072DFA4 /* ProgressIndicator.swift in Sources */ = {isa = PBXBuildFile; fileRef = CA452BAF259FD9770072DFA4 /* ProgressIndicator.swift */; };
|
||||||
|
|
@ -71,7 +70,6 @@
|
||||||
CABFA9CD2592EEEA00380FEE /* Foundation.swift in Sources */ = {isa = PBXBuildFile; fileRef = CABFA9AC2592EEE900380FEE /* Foundation.swift */; };
|
CABFA9CD2592EEEA00380FEE /* Foundation.swift in Sources */ = {isa = PBXBuildFile; fileRef = CABFA9AC2592EEE900380FEE /* Foundation.swift */; };
|
||||||
CABFA9CE2592EEEA00380FEE /* Version+Xcode.swift in Sources */ = {isa = PBXBuildFile; fileRef = CABFA9A62592EEE900380FEE /* Version+Xcode.swift */; };
|
CABFA9CE2592EEEA00380FEE /* Version+Xcode.swift in Sources */ = {isa = PBXBuildFile; fileRef = CABFA9A62592EEE900380FEE /* Version+Xcode.swift */; };
|
||||||
CABFA9CF2592EEEA00380FEE /* Process.swift in Sources */ = {isa = PBXBuildFile; fileRef = CABFA9B42592EEEA00380FEE /* Process.swift */; };
|
CABFA9CF2592EEEA00380FEE /* Process.swift in Sources */ = {isa = PBXBuildFile; fileRef = CABFA9B42592EEEA00380FEE /* Process.swift */; };
|
||||||
CABFA9DF2592F07A00380FEE /* Path in Frameworks */ = {isa = PBXBuildFile; productRef = CABFA9DE2592F07A00380FEE /* Path */; };
|
|
||||||
CABFA9E42592F08E00380FEE /* Version in Frameworks */ = {isa = PBXBuildFile; productRef = CABFA9E32592F08E00380FEE /* Version */; };
|
CABFA9E42592F08E00380FEE /* Version in Frameworks */ = {isa = PBXBuildFile; productRef = CABFA9E32592F08E00380FEE /* Version */; };
|
||||||
CABFA9EE2592F0CC00380FEE /* SwiftSoup in Frameworks */ = {isa = PBXBuildFile; productRef = CABFA9ED2592F0CC00380FEE /* SwiftSoup */; };
|
CABFA9EE2592F0CC00380FEE /* SwiftSoup in Frameworks */ = {isa = PBXBuildFile; productRef = CABFA9ED2592F0CC00380FEE /* SwiftSoup */; };
|
||||||
CABFA9F82592F0F900380FEE /* KeychainAccess in Frameworks */ = {isa = PBXBuildFile; productRef = CABFA9F72592F0F900380FEE /* KeychainAccess */; };
|
CABFA9F82592F0F900380FEE /* KeychainAccess in Frameworks */ = {isa = PBXBuildFile; productRef = CABFA9F72592F0F900380FEE /* KeychainAccess */; };
|
||||||
|
|
@ -108,6 +106,9 @@
|
||||||
E87DD6EB25D053FA00D86808 /* Progress+.swift in Sources */ = {isa = PBXBuildFile; fileRef = E87DD6EA25D053FA00D86808 /* Progress+.swift */; };
|
E87DD6EB25D053FA00D86808 /* Progress+.swift in Sources */ = {isa = PBXBuildFile; fileRef = E87DD6EA25D053FA00D86808 /* Progress+.swift */; };
|
||||||
E89342FA25EDCC17007CF557 /* NotificationManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = E89342F925EDCC17007CF557 /* NotificationManager.swift */; };
|
E89342FA25EDCC17007CF557 /* NotificationManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = E89342F925EDCC17007CF557 /* NotificationManager.swift */; };
|
||||||
E8977EA325C11E1500835F80 /* PreferencesView.swift in Sources */ = {isa = PBXBuildFile; fileRef = E8977EA225C11E1500835F80 /* PreferencesView.swift */; };
|
E8977EA325C11E1500835F80 /* PreferencesView.swift in Sources */ = {isa = PBXBuildFile; fileRef = E8977EA225C11E1500835F80 /* PreferencesView.swift */; };
|
||||||
|
E8B20CBF2A2EDEC20057D816 /* SDKs+Xcode.swift in Sources */ = {isa = PBXBuildFile; fileRef = E8B20CBE2A2EDEC20057D816 /* SDKs+Xcode.swift */; };
|
||||||
|
E8C0EB1A291EF43E0081528A /* XcodesKit in Frameworks */ = {isa = PBXBuildFile; productRef = E8C0EB19291EF43E0081528A /* XcodesKit */; };
|
||||||
|
E8C0EB1C291EF9A10081528A /* AppState+Runtimes.swift in Sources */ = {isa = PBXBuildFile; fileRef = E8C0EB1B291EF9A10081528A /* AppState+Runtimes.swift */; };
|
||||||
E8CBDB8927ADE32300B22292 /* unxip in Copy aria2c */ = {isa = PBXBuildFile; fileRef = E8CBDB8627ADD92000B22292 /* unxip */; settings = {ATTRIBUTES = (CodeSignOnCopy, ); }; };
|
E8CBDB8927ADE32300B22292 /* unxip in Copy aria2c */ = {isa = PBXBuildFile; fileRef = E8CBDB8627ADD92000B22292 /* unxip */; settings = {ATTRIBUTES = (CodeSignOnCopy, ); }; };
|
||||||
E8CBDB8B27AE02FF00B22292 /* ExperiementsPreferencePane.swift in Sources */ = {isa = PBXBuildFile; fileRef = E8CBDB8A27AE02FF00B22292 /* ExperiementsPreferencePane.swift */; };
|
E8CBDB8B27AE02FF00B22292 /* ExperiementsPreferencePane.swift in Sources */ = {isa = PBXBuildFile; fileRef = E8CBDB8A27AE02FF00B22292 /* ExperiementsPreferencePane.swift */; };
|
||||||
E8D0296F284B029800647641 /* BottomStatusBar.swift in Sources */ = {isa = PBXBuildFile; fileRef = E8D0296E284B029800647641 /* BottomStatusBar.swift */; };
|
E8D0296F284B029800647641 /* BottomStatusBar.swift in Sources */ = {isa = PBXBuildFile; fileRef = E8D0296E284B029800647641 /* BottomStatusBar.swift */; };
|
||||||
|
|
@ -115,7 +116,9 @@
|
||||||
E8DA461125FAF7FB002E85EF /* NotificationsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = E8DA461025FAF7FB002E85EF /* NotificationsView.swift */; };
|
E8DA461125FAF7FB002E85EF /* NotificationsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = E8DA461025FAF7FB002E85EF /* NotificationsView.swift */; };
|
||||||
E8E98A9025D8631800EC89A0 /* InstallationStepRowView.swift in Sources */ = {isa = PBXBuildFile; fileRef = CAFBC3FF259AC17F00E2A3D8 /* InstallationStepRowView.swift */; };
|
E8E98A9025D8631800EC89A0 /* InstallationStepRowView.swift in Sources */ = {isa = PBXBuildFile; fileRef = CAFBC3FF259AC17F00E2A3D8 /* InstallationStepRowView.swift */; };
|
||||||
E8E98A9625D863D700EC89A0 /* InstallationStepDetailView.swift in Sources */ = {isa = PBXBuildFile; fileRef = E8E98A9525D863D700EC89A0 /* InstallationStepDetailView.swift */; };
|
E8E98A9625D863D700EC89A0 /* InstallationStepDetailView.swift in Sources */ = {isa = PBXBuildFile; fileRef = E8E98A9525D863D700EC89A0 /* InstallationStepDetailView.swift */; };
|
||||||
|
E8F44A1E296B4CD7002D6592 /* Path in Frameworks */ = {isa = PBXBuildFile; productRef = E8F44A1D296B4CD7002D6592 /* Path */; };
|
||||||
E8F81FC4282D8A17006CBD0F /* Sparkle in Frameworks */ = {isa = PBXBuildFile; productRef = E8F81FC3282D8A17006CBD0F /* Sparkle */; };
|
E8F81FC4282D8A17006CBD0F /* Sparkle in Frameworks */ = {isa = PBXBuildFile; productRef = E8F81FC3282D8A17006CBD0F /* Sparkle */; };
|
||||||
|
E8FD5727291EE4AC001E004C /* AsyncNetworkService in Frameworks */ = {isa = PBXBuildFile; productRef = E8FD5726291EE4AC001E004C /* AsyncNetworkService */; };
|
||||||
/* End PBXBuildFile section */
|
/* End PBXBuildFile section */
|
||||||
|
|
||||||
/* Begin PBXContainerItemProxy section */
|
/* Begin PBXContainerItemProxy section */
|
||||||
|
|
@ -196,7 +199,6 @@
|
||||||
CA25192925A9644800F08414 /* XcodeInstallState.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = XcodeInstallState.swift; sourceTree = "<group>"; };
|
CA25192925A9644800F08414 /* XcodeInstallState.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = XcodeInstallState.swift; sourceTree = "<group>"; };
|
||||||
CA378F982466567600A58CE0 /* AppState.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppState.swift; sourceTree = "<group>"; };
|
CA378F982466567600A58CE0 /* AppState.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppState.swift; sourceTree = "<group>"; };
|
||||||
CA39711824495F0E00AFFB77 /* AppStoreButtonStyle.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppStoreButtonStyle.swift; sourceTree = "<group>"; };
|
CA39711824495F0E00AFFB77 /* AppStoreButtonStyle.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppStoreButtonStyle.swift; sourceTree = "<group>"; };
|
||||||
CA42DD6D25AEA8B200BC0B0C /* Logger.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Logger.swift; sourceTree = "<group>"; };
|
|
||||||
CA42DD7225AEB04300BC0B0C /* Logger.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Logger.swift; sourceTree = "<group>"; };
|
CA42DD7225AEB04300BC0B0C /* Logger.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Logger.swift; sourceTree = "<group>"; };
|
||||||
CA44901E2463AD34003D8213 /* Tag.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Tag.swift; sourceTree = "<group>"; };
|
CA44901E2463AD34003D8213 /* Tag.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Tag.swift; sourceTree = "<group>"; };
|
||||||
CA452BAF259FD9770072DFA4 /* ProgressIndicator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProgressIndicator.swift; sourceTree = "<group>"; };
|
CA452BAF259FD9770072DFA4 /* ProgressIndicator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProgressIndicator.swift; sourceTree = "<group>"; };
|
||||||
|
|
@ -297,11 +299,14 @@
|
||||||
CAFFFEEE259CEAC400903F81 /* RingProgressViewStyle.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RingProgressViewStyle.swift; sourceTree = "<group>"; };
|
CAFFFEEE259CEAC400903F81 /* RingProgressViewStyle.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RingProgressViewStyle.swift; sourceTree = "<group>"; };
|
||||||
E2AFDCCA28F024D000864ADD /* nl */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = nl; path = nl.lproj/Localizable.strings; sourceTree = "<group>"; };
|
E2AFDCCA28F024D000864ADD /* nl */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = nl; path = nl.lproj/Localizable.strings; sourceTree = "<group>"; };
|
||||||
E81D7E9F2805250100A205FC /* Collection+.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Collection+.swift"; sourceTree = "<group>"; };
|
E81D7E9F2805250100A205FC /* Collection+.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Collection+.swift"; sourceTree = "<group>"; };
|
||||||
|
E856BB73291EDD3D00DC438B /* XcodesKit */ = {isa = PBXFileReference; lastKnownFileType = wrapper; name = XcodesKit; path = Xcodes/XcodesKit; sourceTree = "<group>"; };
|
||||||
E872EE4F2808D4F100D3DD8B /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/Localizable.strings; sourceTree = "<group>"; };
|
E872EE4F2808D4F100D3DD8B /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/Localizable.strings; sourceTree = "<group>"; };
|
||||||
E87AB3C42939B65E00D72F43 /* Hardware.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Hardware.swift; sourceTree = "<group>"; };
|
E87AB3C42939B65E00D72F43 /* Hardware.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Hardware.swift; sourceTree = "<group>"; };
|
||||||
E87DD6EA25D053FA00D86808 /* Progress+.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Progress+.swift"; sourceTree = "<group>"; };
|
E87DD6EA25D053FA00D86808 /* Progress+.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Progress+.swift"; sourceTree = "<group>"; };
|
||||||
E89342F925EDCC17007CF557 /* NotificationManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotificationManager.swift; sourceTree = "<group>"; };
|
E89342F925EDCC17007CF557 /* NotificationManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotificationManager.swift; sourceTree = "<group>"; };
|
||||||
E8977EA225C11E1500835F80 /* PreferencesView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PreferencesView.swift; sourceTree = "<group>"; };
|
E8977EA225C11E1500835F80 /* PreferencesView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PreferencesView.swift; sourceTree = "<group>"; };
|
||||||
|
E8B20CBE2A2EDEC20057D816 /* SDKs+Xcode.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "SDKs+Xcode.swift"; sourceTree = "<group>"; };
|
||||||
|
E8C0EB1B291EF9A10081528A /* AppState+Runtimes.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "AppState+Runtimes.swift"; sourceTree = "<group>"; };
|
||||||
E8CBDB8627ADD92000B22292 /* unxip */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.executable"; path = unxip; sourceTree = "<group>"; };
|
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>"; };
|
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>"; };
|
E8D0296E284B029800647641 /* BottomStatusBar.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BottomStatusBar.swift; sourceTree = "<group>"; };
|
||||||
|
|
@ -328,9 +333,11 @@
|
||||||
CA9FF86D25951C6E00E47BAF /* XCModel in Frameworks */,
|
CA9FF86D25951C6E00E47BAF /* XCModel in Frameworks */,
|
||||||
CABFA9F82592F0F900380FEE /* KeychainAccess in Frameworks */,
|
CABFA9F82592F0F900380FEE /* KeychainAccess in Frameworks */,
|
||||||
CAA858CD25A3D8BC00ACF8C0 /* ErrorHandling in Frameworks */,
|
CAA858CD25A3D8BC00ACF8C0 /* ErrorHandling in Frameworks */,
|
||||||
|
E8C0EB1A291EF43E0081528A /* XcodesKit in Frameworks */,
|
||||||
|
E8FD5727291EE4AC001E004C /* AsyncNetworkService in Frameworks */,
|
||||||
CAA1CB2D255A5262003FD669 /* AppleAPI in Frameworks */,
|
CAA1CB2D255A5262003FD669 /* AppleAPI in Frameworks */,
|
||||||
CABFA9DF2592F07A00380FEE /* Path in Frameworks */,
|
|
||||||
CABFA9EE2592F0CC00380FEE /* SwiftSoup in Frameworks */,
|
CABFA9EE2592F0CC00380FEE /* SwiftSoup in Frameworks */,
|
||||||
|
E8F44A1E296B4CD7002D6592 /* Path in Frameworks */,
|
||||||
);
|
);
|
||||||
runOnlyForDeploymentPostprocessing = 0;
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
};
|
};
|
||||||
|
|
@ -452,6 +459,7 @@
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
CA378F982466567600A58CE0 /* AppState.swift */,
|
CA378F982466567600A58CE0 /* AppState.swift */,
|
||||||
|
E8C0EB1B291EF9A10081528A /* AppState+Runtimes.swift */,
|
||||||
CAE424B3259A764700B8B246 /* AppState+Install.swift */,
|
CAE424B3259A764700B8B246 /* AppState+Install.swift */,
|
||||||
CABFA9A72592EEE900380FEE /* AppState+Update.swift */,
|
CABFA9A72592EEE900380FEE /* AppState+Update.swift */,
|
||||||
CAA8589A25A2B83000ACF8C0 /* Aria2CError.swift */,
|
CAA8589A25A2B83000ACF8C0 /* Aria2CError.swift */,
|
||||||
|
|
@ -473,7 +481,6 @@
|
||||||
CAC281D9259F985100B8AB0B /* InstallationStep.swift */,
|
CAC281D9259F985100B8AB0B /* InstallationStep.swift */,
|
||||||
CA9FF8862595607900E47BAF /* InstalledXcode.swift */,
|
CA9FF8862595607900E47BAF /* InstalledXcode.swift */,
|
||||||
CAA8587B25A2B37900ACF8C0 /* IsTesting.swift */,
|
CAA8587B25A2B37900ACF8C0 /* IsTesting.swift */,
|
||||||
CA42DD6D25AEA8B200BC0B0C /* Logger.swift */,
|
|
||||||
E89342F925EDCC17007CF557 /* NotificationManager.swift */,
|
E89342F925EDCC17007CF557 /* NotificationManager.swift */,
|
||||||
CAE4248B259A68B800B8B246 /* Optional+IsNotNil.swift */,
|
CAE4248B259A68B800B8B246 /* Optional+IsNotNil.swift */,
|
||||||
CABFA9AE2592EEE900380FEE /* Path+.swift */,
|
CABFA9AE2592EEE900380FEE /* Path+.swift */,
|
||||||
|
|
@ -492,6 +499,7 @@
|
||||||
E81D7E9F2805250100A205FC /* Collection+.swift */,
|
E81D7E9F2805250100A205FC /* Collection+.swift */,
|
||||||
E8D655BF288DD04700A139C2 /* SelectedActionType.swift */,
|
E8D655BF288DD04700A139C2 /* SelectedActionType.swift */,
|
||||||
E87AB3C42939B65E00D72F43 /* Hardware.swift */,
|
E87AB3C42939B65E00D72F43 /* Hardware.swift */,
|
||||||
|
E8B20CBE2A2EDEC20057D816 /* SDKs+Xcode.swift */,
|
||||||
);
|
);
|
||||||
path = Backend;
|
path = Backend;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
|
|
@ -532,6 +540,7 @@
|
||||||
CAD2E7952449574E00113D76 = {
|
CAD2E7952449574E00113D76 = {
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
|
E856BB73291EDD3D00DC438B /* XcodesKit */,
|
||||||
CA8FB5F8256E0F9400469DA5 /* README.md */,
|
CA8FB5F8256E0F9400469DA5 /* README.md */,
|
||||||
CABFA9D42592EF6300380FEE /* DECISIONS.md */,
|
CABFA9D42592EF6300380FEE /* DECISIONS.md */,
|
||||||
CABFA9A02592EAF500380FEE /* R&PLogo.png */,
|
CABFA9A02592EAF500380FEE /* R&PLogo.png */,
|
||||||
|
|
@ -656,7 +665,6 @@
|
||||||
name = Xcodes;
|
name = Xcodes;
|
||||||
packageProductDependencies = (
|
packageProductDependencies = (
|
||||||
CAA1CB2C255A5262003FD669 /* AppleAPI */,
|
CAA1CB2C255A5262003FD669 /* AppleAPI */,
|
||||||
CABFA9DE2592F07A00380FEE /* Path */,
|
|
||||||
CABFA9E32592F08E00380FEE /* Version */,
|
CABFA9E32592F08E00380FEE /* Version */,
|
||||||
CABFA9ED2592F0CC00380FEE /* SwiftSoup */,
|
CABFA9ED2592F0CC00380FEE /* SwiftSoup */,
|
||||||
CABFA9F72592F0F900380FEE /* KeychainAccess */,
|
CABFA9F72592F0F900380FEE /* KeychainAccess */,
|
||||||
|
|
@ -664,6 +672,9 @@
|
||||||
CA9FF86C25951C6E00E47BAF /* XCModel */,
|
CA9FF86C25951C6E00E47BAF /* XCModel */,
|
||||||
CAA858CC25A3D8BC00ACF8C0 /* ErrorHandling */,
|
CAA858CC25A3D8BC00ACF8C0 /* ErrorHandling */,
|
||||||
E8F81FC3282D8A17006CBD0F /* Sparkle */,
|
E8F81FC3282D8A17006CBD0F /* Sparkle */,
|
||||||
|
E8FD5726291EE4AC001E004C /* AsyncNetworkService */,
|
||||||
|
E8C0EB19291EF43E0081528A /* XcodesKit */,
|
||||||
|
E8F44A1D296B4CD7002D6592 /* Path */,
|
||||||
);
|
);
|
||||||
productName = XcodesMac;
|
productName = XcodesMac;
|
||||||
productReference = CAD2E79E2449574E00113D76 /* Xcodes.app */;
|
productReference = CAD2E79E2449574E00113D76 /* Xcodes.app */;
|
||||||
|
|
@ -737,7 +748,6 @@
|
||||||
);
|
);
|
||||||
mainGroup = CAD2E7952449574E00113D76;
|
mainGroup = CAD2E7952449574E00113D76;
|
||||||
packageReferences = (
|
packageReferences = (
|
||||||
CABFA9DD2592F07A00380FEE /* XCRemoteSwiftPackageReference "Path" */,
|
|
||||||
CABFA9E22592F08E00380FEE /* XCRemoteSwiftPackageReference "Version" */,
|
CABFA9E22592F08E00380FEE /* XCRemoteSwiftPackageReference "Version" */,
|
||||||
CABFA9EC2592F0CC00380FEE /* XCRemoteSwiftPackageReference "SwiftSoup" */,
|
CABFA9EC2592F0CC00380FEE /* XCRemoteSwiftPackageReference "SwiftSoup" */,
|
||||||
CABFA9F62592F0F900380FEE /* XCRemoteSwiftPackageReference "KeychainAccess" */,
|
CABFA9F62592F0F900380FEE /* XCRemoteSwiftPackageReference "KeychainAccess" */,
|
||||||
|
|
@ -746,6 +756,8 @@
|
||||||
CAA858CB25A3D8BC00ACF8C0 /* XCRemoteSwiftPackageReference "ErrorHandling" */,
|
CAA858CB25A3D8BC00ACF8C0 /* XCRemoteSwiftPackageReference "ErrorHandling" */,
|
||||||
CAC28186259EE27200B8AB0B /* XCRemoteSwiftPackageReference "CombineExpectations" */,
|
CAC28186259EE27200B8AB0B /* XCRemoteSwiftPackageReference "CombineExpectations" */,
|
||||||
E8F81FC2282D8A17006CBD0F /* XCRemoteSwiftPackageReference "Sparkle" */,
|
E8F81FC2282D8A17006CBD0F /* XCRemoteSwiftPackageReference "Sparkle" */,
|
||||||
|
E8FD5725291EE4AC001E004C /* XCRemoteSwiftPackageReference "AsyncHTTPNetworkService" */,
|
||||||
|
E8F44A1C296B4CD7002D6592 /* XCRemoteSwiftPackageReference "Path" */,
|
||||||
);
|
);
|
||||||
productRefGroup = CAD2E79F2449574E00113D76 /* Products */;
|
productRefGroup = CAD2E79F2449574E00113D76 /* Products */;
|
||||||
projectDirPath = "";
|
projectDirPath = "";
|
||||||
|
|
@ -854,7 +866,6 @@
|
||||||
36741BFF291E50F500A85AAE /* FileError.swift in Sources */,
|
36741BFF291E50F500A85AAE /* FileError.swift in Sources */,
|
||||||
CA9FF8872595607900E47BAF /* InstalledXcode.swift in Sources */,
|
CA9FF8872595607900E47BAF /* InstalledXcode.swift in Sources */,
|
||||||
53CBAB2C263DCC9100410495 /* XcodesAlert.swift in Sources */,
|
53CBAB2C263DCC9100410495 /* XcodesAlert.swift in Sources */,
|
||||||
CA42DD6E25AEA8B200BC0B0C /* Logger.swift in Sources */,
|
|
||||||
CA61A6E0259835580008926E /* Xcode.swift in Sources */,
|
CA61A6E0259835580008926E /* Xcode.swift in Sources */,
|
||||||
CAE4247F259A666100B8B246 /* MainWindow.swift in Sources */,
|
CAE4247F259A666100B8B246 /* MainWindow.swift in Sources */,
|
||||||
CA452BB0259FD9770072DFA4 /* ProgressIndicator.swift in Sources */,
|
CA452BB0259FD9770072DFA4 /* ProgressIndicator.swift in Sources */,
|
||||||
|
|
@ -871,6 +882,7 @@
|
||||||
E8DA461125FAF7FB002E85EF /* NotificationsView.swift in Sources */,
|
E8DA461125FAF7FB002E85EF /* NotificationsView.swift in Sources */,
|
||||||
CAA1CB35255A5AD5003FD669 /* SignInCredentialsView.swift in Sources */,
|
CAA1CB35255A5AD5003FD669 /* SignInCredentialsView.swift in Sources */,
|
||||||
E81D7EA02805250100A205FC /* Collection+.swift in Sources */,
|
E81D7EA02805250100A205FC /* Collection+.swift in Sources */,
|
||||||
|
E8B20CBF2A2EDEC20057D816 /* SDKs+Xcode.swift in Sources */,
|
||||||
CA9FF877259528CC00E47BAF /* Version+XcodeReleases.swift in Sources */,
|
CA9FF877259528CC00E47BAF /* Version+XcodeReleases.swift in Sources */,
|
||||||
CABFAA2D2592FBFC00380FEE /* Configure.swift in Sources */,
|
CABFAA2D2592FBFC00380FEE /* Configure.swift in Sources */,
|
||||||
CA73510D257BFCEF00EA9CF8 /* NSAttributedString+.swift in Sources */,
|
CA73510D257BFCEF00EA9CF8 /* NSAttributedString+.swift in Sources */,
|
||||||
|
|
@ -894,6 +906,7 @@
|
||||||
CABFA9C92592EEEA00380FEE /* URLRequest+Apple.swift in Sources */,
|
CABFA9C92592EEEA00380FEE /* URLRequest+Apple.swift in Sources */,
|
||||||
CABFAA432593104F00380FEE /* AboutView.swift in Sources */,
|
CABFAA432593104F00380FEE /* AboutView.swift in Sources */,
|
||||||
E8D0296F284B029800647641 /* BottomStatusBar.swift in Sources */,
|
E8D0296F284B029800647641 /* BottomStatusBar.swift in Sources */,
|
||||||
|
E8C0EB1C291EF9A10081528A /* AppState+Runtimes.swift in Sources */,
|
||||||
E8E98A9025D8631800EC89A0 /* InstallationStepRowView.swift in Sources */,
|
E8E98A9025D8631800EC89A0 /* InstallationStepRowView.swift in Sources */,
|
||||||
CABFA9CC2592EEEA00380FEE /* Path+.swift in Sources */,
|
CABFA9CC2592EEEA00380FEE /* Path+.swift in Sources */,
|
||||||
CAD2E7A22449574E00113D76 /* XcodesApp.swift in Sources */,
|
CAD2E7A22449574E00113D76 /* XcodesApp.swift in Sources */,
|
||||||
|
|
@ -1416,14 +1429,6 @@
|
||||||
minimumVersion = 0.1.0;
|
minimumVersion = 0.1.0;
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
CABFA9DD2592F07A00380FEE /* XCRemoteSwiftPackageReference "Path" */ = {
|
|
||||||
isa = XCRemoteSwiftPackageReference;
|
|
||||||
repositoryURL = "https://github.com/mxcl/Path.swift";
|
|
||||||
requirement = {
|
|
||||||
kind = upToNextMinorVersion;
|
|
||||||
minimumVersion = 0.16.0;
|
|
||||||
};
|
|
||||||
};
|
|
||||||
CABFA9E22592F08E00380FEE /* XCRemoteSwiftPackageReference "Version" */ = {
|
CABFA9E22592F08E00380FEE /* XCRemoteSwiftPackageReference "Version" */ = {
|
||||||
isa = XCRemoteSwiftPackageReference;
|
isa = XCRemoteSwiftPackageReference;
|
||||||
repositoryURL = "https://github.com/mxcl/Version";
|
repositoryURL = "https://github.com/mxcl/Version";
|
||||||
|
|
@ -1464,6 +1469,14 @@
|
||||||
minimumVersion = 0.6.0;
|
minimumVersion = 0.6.0;
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
E8F44A1C296B4CD7002D6592 /* XCRemoteSwiftPackageReference "Path" */ = {
|
||||||
|
isa = XCRemoteSwiftPackageReference;
|
||||||
|
repositoryURL = "https://github.com/mxcl/Path.swift";
|
||||||
|
requirement = {
|
||||||
|
kind = upToNextMajorVersion;
|
||||||
|
minimumVersion = 1.0.0;
|
||||||
|
};
|
||||||
|
};
|
||||||
E8F81FC2282D8A17006CBD0F /* XCRemoteSwiftPackageReference "Sparkle" */ = {
|
E8F81FC2282D8A17006CBD0F /* XCRemoteSwiftPackageReference "Sparkle" */ = {
|
||||||
isa = XCRemoteSwiftPackageReference;
|
isa = XCRemoteSwiftPackageReference;
|
||||||
repositoryURL = "https://github.com/sparkle-project/Sparkle";
|
repositoryURL = "https://github.com/sparkle-project/Sparkle";
|
||||||
|
|
@ -1472,6 +1485,14 @@
|
||||||
minimumVersion = 2.0.0;
|
minimumVersion = 2.0.0;
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
E8FD5725291EE4AC001E004C /* XCRemoteSwiftPackageReference "AsyncHTTPNetworkService" */ = {
|
||||||
|
isa = XCRemoteSwiftPackageReference;
|
||||||
|
repositoryURL = "https://github.com/RobotsAndPencils/AsyncHTTPNetworkService";
|
||||||
|
requirement = {
|
||||||
|
branch = main;
|
||||||
|
kind = branch;
|
||||||
|
};
|
||||||
|
};
|
||||||
/* End XCRemoteSwiftPackageReference section */
|
/* End XCRemoteSwiftPackageReference section */
|
||||||
|
|
||||||
/* Begin XCSwiftPackageProductDependency section */
|
/* Begin XCSwiftPackageProductDependency section */
|
||||||
|
|
@ -1489,11 +1510,6 @@
|
||||||
package = CAA858CB25A3D8BC00ACF8C0 /* XCRemoteSwiftPackageReference "ErrorHandling" */;
|
package = CAA858CB25A3D8BC00ACF8C0 /* XCRemoteSwiftPackageReference "ErrorHandling" */;
|
||||||
productName = ErrorHandling;
|
productName = ErrorHandling;
|
||||||
};
|
};
|
||||||
CABFA9DE2592F07A00380FEE /* Path */ = {
|
|
||||||
isa = XCSwiftPackageProductDependency;
|
|
||||||
package = CABFA9DD2592F07A00380FEE /* XCRemoteSwiftPackageReference "Path" */;
|
|
||||||
productName = Path;
|
|
||||||
};
|
|
||||||
CABFA9E32592F08E00380FEE /* Version */ = {
|
CABFA9E32592F08E00380FEE /* Version */ = {
|
||||||
isa = XCSwiftPackageProductDependency;
|
isa = XCSwiftPackageProductDependency;
|
||||||
package = CABFA9E22592F08E00380FEE /* XCRemoteSwiftPackageReference "Version" */;
|
package = CABFA9E22592F08E00380FEE /* XCRemoteSwiftPackageReference "Version" */;
|
||||||
|
|
@ -1519,11 +1535,25 @@
|
||||||
package = CAC28186259EE27200B8AB0B /* XCRemoteSwiftPackageReference "CombineExpectations" */;
|
package = CAC28186259EE27200B8AB0B /* XCRemoteSwiftPackageReference "CombineExpectations" */;
|
||||||
productName = CombineExpectations;
|
productName = CombineExpectations;
|
||||||
};
|
};
|
||||||
|
E8C0EB19291EF43E0081528A /* XcodesKit */ = {
|
||||||
|
isa = XCSwiftPackageProductDependency;
|
||||||
|
productName = XcodesKit;
|
||||||
|
};
|
||||||
|
E8F44A1D296B4CD7002D6592 /* Path */ = {
|
||||||
|
isa = XCSwiftPackageProductDependency;
|
||||||
|
package = E8F44A1C296B4CD7002D6592 /* XCRemoteSwiftPackageReference "Path" */;
|
||||||
|
productName = Path;
|
||||||
|
};
|
||||||
E8F81FC3282D8A17006CBD0F /* Sparkle */ = {
|
E8F81FC3282D8A17006CBD0F /* Sparkle */ = {
|
||||||
isa = XCSwiftPackageProductDependency;
|
isa = XCSwiftPackageProductDependency;
|
||||||
package = E8F81FC2282D8A17006CBD0F /* XCRemoteSwiftPackageReference "Sparkle" */;
|
package = E8F81FC2282D8A17006CBD0F /* XCRemoteSwiftPackageReference "Sparkle" */;
|
||||||
productName = Sparkle;
|
productName = Sparkle;
|
||||||
};
|
};
|
||||||
|
E8FD5726291EE4AC001E004C /* AsyncNetworkService */ = {
|
||||||
|
isa = XCSwiftPackageProductDependency;
|
||||||
|
package = E8FD5725291EE4AC001E004C /* XCRemoteSwiftPackageReference "AsyncHTTPNetworkService" */;
|
||||||
|
productName = AsyncNetworkService;
|
||||||
|
};
|
||||||
/* End XCSwiftPackageProductDependency section */
|
/* End XCSwiftPackageProductDependency section */
|
||||||
};
|
};
|
||||||
rootObject = CAD2E7962449574E00113D76 /* Project object */;
|
rootObject = CAD2E7962449574E00113D76 /* Project object */;
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,15 @@
|
||||||
{
|
{
|
||||||
"object": {
|
"object": {
|
||||||
"pins": [
|
"pins": [
|
||||||
|
{
|
||||||
|
"package": "AsyncNetworkService",
|
||||||
|
"repositoryURL": "https://github.com/RobotsAndPencils/AsyncHTTPNetworkService",
|
||||||
|
"state": {
|
||||||
|
"branch": "main",
|
||||||
|
"revision": "97770856c4e429f880d4b4dd68cfaf286dc00c30",
|
||||||
|
"version": null
|
||||||
|
}
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"package": "CombineExpectations",
|
"package": "CombineExpectations",
|
||||||
"repositoryURL": "https://github.com/groue/CombineExpectations",
|
"repositoryURL": "https://github.com/groue/CombineExpectations",
|
||||||
|
|
@ -51,8 +60,8 @@
|
||||||
"repositoryURL": "https://github.com/mxcl/Path.swift",
|
"repositoryURL": "https://github.com/mxcl/Path.swift",
|
||||||
"state": {
|
"state": {
|
||||||
"branch": null,
|
"branch": null,
|
||||||
"revision": "dac007e907a4f4c565cfdc55a9ce148a761a11d5",
|
"revision": "9c6f807b0a76be0e27aecc908bc6f173400d839e",
|
||||||
"version": "0.16.3"
|
"version": "1.4.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,7 @@ import AppleAPI
|
||||||
import Version
|
import Version
|
||||||
import LegibleError
|
import LegibleError
|
||||||
import os.log
|
import os.log
|
||||||
|
import XcodesKit
|
||||||
|
|
||||||
/// Downloads and installs Xcodes
|
/// Downloads and installs Xcodes
|
||||||
extension AppState {
|
extension AppState {
|
||||||
|
|
|
||||||
32
Xcodes/Backend/AppState+Runtimes.swift
Normal file
32
Xcodes/Backend/AppState+Runtimes.swift
Normal file
|
|
@ -0,0 +1,32 @@
|
||||||
|
import Foundation
|
||||||
|
import XcodesKit
|
||||||
|
import OSLog
|
||||||
|
|
||||||
|
extension AppState {
|
||||||
|
func updateDownloadableRuntimes() {
|
||||||
|
Task {
|
||||||
|
do {
|
||||||
|
let runtimes = try await self.runtimeService.downloadableRuntimes().downloadables
|
||||||
|
DispatchQueue.main.async {
|
||||||
|
self.downloadableRuntimes = runtimes
|
||||||
|
}
|
||||||
|
try? cacheDownloadableRuntimes(runtimes)
|
||||||
|
} catch {
|
||||||
|
Logger.appState.error("Error downloading runtimes: \(error.localizedDescription)")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func updateInstalledRuntimes() {
|
||||||
|
Task {
|
||||||
|
do {
|
||||||
|
let runtimes = try await self.runtimeService.localInstalledRuntimes()
|
||||||
|
DispatchQueue.main.async {
|
||||||
|
self.installedRuntimes = runtimes
|
||||||
|
}
|
||||||
|
} catch {
|
||||||
|
Logger.appState.error("Error loading installed runtimes: \(error.localizedDescription)")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -5,6 +5,7 @@ import Version
|
||||||
import SwiftSoup
|
import SwiftSoup
|
||||||
import struct XCModel.Xcode
|
import struct XCModel.Xcode
|
||||||
import AppleAPI
|
import AppleAPI
|
||||||
|
import XcodesKit
|
||||||
|
|
||||||
extension AppState {
|
extension AppState {
|
||||||
|
|
||||||
|
|
@ -36,6 +37,8 @@ extension AppState {
|
||||||
|
|
||||||
func update() {
|
func update() {
|
||||||
guard !isUpdating else { return }
|
guard !isUpdating else { return }
|
||||||
|
updateDownloadableRuntimes()
|
||||||
|
updateInstalledRuntimes()
|
||||||
updatePublisher = updateSelectedXcodePath()
|
updatePublisher = updateSelectedXcodePath()
|
||||||
.flatMap { _ in
|
.flatMap { _ in
|
||||||
self.updateAvailableXcodes(from: self.dataSource)
|
self.updateAvailableXcodes(from: self.dataSource)
|
||||||
|
|
@ -125,6 +128,21 @@ extension AppState {
|
||||||
withIntermediateDirectories: true)
|
withIntermediateDirectories: true)
|
||||||
try data.write(to: Path.cacheFile.url)
|
try data.write(to: Path.cacheFile.url)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// MARK: Runtime Cache
|
||||||
|
|
||||||
|
func loadCacheDownloadableRuntimes() throws {
|
||||||
|
guard let data = Current.files.contents(atPath: Path.runtimeCacheFile.string) else { return }
|
||||||
|
let runtimes = try JSONDecoder().decode([DownloadableRuntime].self, from: data)
|
||||||
|
self.downloadableRuntimes = runtimes
|
||||||
|
}
|
||||||
|
|
||||||
|
func cacheDownloadableRuntimes(_ runtimes: [DownloadableRuntime]) throws {
|
||||||
|
let data = try JSONEncoder().encode(runtimes)
|
||||||
|
try FileManager.default.createDirectory(at: Path.runtimeCacheFile.url.deletingLastPathComponent(),
|
||||||
|
withIntermediateDirectories: true)
|
||||||
|
try data.write(to: Path.runtimeCacheFile.url)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
extension AppState {
|
extension AppState {
|
||||||
|
|
|
||||||
|
|
@ -7,9 +7,11 @@ import KeychainAccess
|
||||||
import Path
|
import Path
|
||||||
import Version
|
import Version
|
||||||
import os.log
|
import os.log
|
||||||
|
import XcodesKit
|
||||||
|
|
||||||
class AppState: ObservableObject {
|
class AppState: ObservableObject {
|
||||||
private let client = AppleAPI.Client()
|
private let client = AppleAPI.Client()
|
||||||
|
internal let runtimeService = RuntimeService()
|
||||||
|
|
||||||
// MARK: - Published Properties
|
// MARK: - Published Properties
|
||||||
|
|
||||||
|
|
@ -99,6 +101,12 @@ class AppState: ObservableObject {
|
||||||
Current.defaults.set(showOpenInRosettaOption, forKey: "showOpenInRosettaOption")
|
Current.defaults.set(showOpenInRosettaOption, forKey: "showOpenInRosettaOption")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// MARK: - Runtimes
|
||||||
|
|
||||||
|
@Published var downloadableRuntimes: [DownloadableRuntime] = []
|
||||||
|
@Published var installedRuntimes: [CoreSimulatorRuntimeInfo] = []
|
||||||
|
|
||||||
// MARK: - Publisher Cancellables
|
// MARK: - Publisher Cancellables
|
||||||
|
|
||||||
var cancellables = Set<AnyCancellable>()
|
var cancellables = Set<AnyCancellable>()
|
||||||
|
|
@ -136,6 +144,7 @@ class AppState: ObservableObject {
|
||||||
init() {
|
init() {
|
||||||
guard !isTesting else { return }
|
guard !isTesting else { return }
|
||||||
try? loadCachedAvailableXcodes()
|
try? loadCachedAvailableXcodes()
|
||||||
|
try? loadCacheDownloadableRuntimes()
|
||||||
checkIfHelperIsInstalled()
|
checkIfHelperIsInstalled()
|
||||||
setupAutoInstallTimer()
|
setupAutoInstallTimer()
|
||||||
setupDefaults()
|
setupDefaults()
|
||||||
|
|
@ -783,6 +792,21 @@ class AppState: ObservableObject {
|
||||||
self.allXcodes = newAllXcodes.sorted { $0.version > $1.version }
|
self.allXcodes = newAllXcodes.sorted { $0.version > $1.version }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// MARK: Runtimes
|
||||||
|
func getRunTimes(xcode: Xcode) -> [DownloadableRuntime] {
|
||||||
|
|
||||||
|
let builds = xcode.sdks?.allBuilds()
|
||||||
|
|
||||||
|
let runtime = builds?.flatMap { sdkBuild in
|
||||||
|
downloadableRuntimes.filter {
|
||||||
|
$0.simulatorVersion.buildUpdate == sdkBuild
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// appState.installedRuntimes has a list of builds that user has installed.
|
||||||
|
|
||||||
|
return runtime ?? []
|
||||||
|
}
|
||||||
|
|
||||||
// MARK: - Private
|
// MARK: - Private
|
||||||
|
|
||||||
private func uninstallXcode(path: Path) -> AnyPublisher<Void, Error> {
|
private func uninstallXcode(path: Path) -> AnyPublisher<Void, Error> {
|
||||||
|
|
|
||||||
|
|
@ -1,13 +1,13 @@
|
||||||
import Foundation
|
import Foundation
|
||||||
import Path
|
import Path
|
||||||
|
|
||||||
extension Entry {
|
extension Path {
|
||||||
static func isAppBundle(kind: Kind, path: Path) -> Bool {
|
static func isAppBundle(path: Path) -> Bool {
|
||||||
kind == .directory &&
|
path.isDirectory &&
|
||||||
path.extension == "app" &&
|
path.extension == "app" &&
|
||||||
!path.isSymlink
|
!path.isSymlink
|
||||||
}
|
}
|
||||||
static func infoPlist(kind: Kind, path: Path) -> InfoPlist? {
|
static func infoPlist(path: Path) -> InfoPlist? {
|
||||||
let infoPlistPath = path.join("Contents").join("Info.plist")
|
let infoPlistPath = path.join("Contents").join("Info.plist")
|
||||||
guard
|
guard
|
||||||
let infoPlistData = try? Data(contentsOf: infoPlistPath.url),
|
let infoPlistData = try? Data(contentsOf: infoPlistPath.url),
|
||||||
|
|
@ -18,10 +18,10 @@ extension Entry {
|
||||||
}
|
}
|
||||||
|
|
||||||
var isAppBundle: Bool {
|
var isAppBundle: Bool {
|
||||||
Entry.isAppBundle(kind: kind, path: path)
|
Path.isAppBundle(path: self)
|
||||||
}
|
}
|
||||||
|
|
||||||
var infoPlist: InfoPlist? {
|
var infoPlist: InfoPlist? {
|
||||||
Entry.infoPlist(kind: kind, path: path)
|
Path.infoPlist(path: self)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@ import Foundation
|
||||||
import Path
|
import Path
|
||||||
import AppleAPI
|
import AppleAPI
|
||||||
import KeychainAccess
|
import KeychainAccess
|
||||||
|
import XcodesKit
|
||||||
/**
|
/**
|
||||||
Lightweight dependency injection using global mutable state :P
|
Lightweight dependency injection using global mutable state :P
|
||||||
|
|
||||||
|
|
@ -166,7 +166,7 @@ public struct Files {
|
||||||
public var installedXcodes = _installedXcodes
|
public var installedXcodes = _installedXcodes
|
||||||
|
|
||||||
public func installedXcode(destination: Path) -> InstalledXcode? {
|
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" {
|
if Path.isAppBundle(path: destination) && Path.infoPlist(path: destination)?.bundleID == "com.apple.dt.Xcode" {
|
||||||
return InstalledXcode.init(path: destination)
|
return InstalledXcode.init(path: destination)
|
||||||
} else {
|
} else {
|
||||||
return nil
|
return nil
|
||||||
|
|
@ -175,9 +175,9 @@ public struct Files {
|
||||||
}
|
}
|
||||||
|
|
||||||
private func _installedXcodes(destination: Path) -> [InstalledXcode] {
|
private func _installedXcodes(destination: Path) -> [InstalledXcode] {
|
||||||
((try? destination.ls()) ?? [])
|
destination.ls()
|
||||||
.filter { $0.isAppBundle && $0.infoPlist?.bundleID == "com.apple.dt.Xcode" }
|
.filter { $0.isAppBundle && $0.infoPlist?.bundleID == "com.apple.dt.Xcode" }
|
||||||
.map { $0.path }
|
.map { $0 }
|
||||||
.compactMap(InstalledXcode.init)
|
.compactMap(InstalledXcode.init)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,10 +0,0 @@
|
||||||
import Foundation
|
|
||||||
import os.log
|
|
||||||
|
|
||||||
extension Logger {
|
|
||||||
private static var subsystem = Bundle.main.bundleIdentifier!
|
|
||||||
|
|
||||||
static let appState = Logger(subsystem: subsystem, category: "appState")
|
|
||||||
static let helperClient = Logger(subsystem: subsystem, category: "helperClient")
|
|
||||||
static let subprocess = Logger(subsystem: subsystem, category: "subprocess")
|
|
||||||
}
|
|
||||||
|
|
@ -28,4 +28,8 @@ extension Path {
|
||||||
}
|
}
|
||||||
return path
|
return path
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static var runtimeCacheFile: Path {
|
||||||
|
return xcodesApplicationSupport/"downloadable-runtimes.json"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -2,12 +2,11 @@ import Combine
|
||||||
import Foundation
|
import Foundation
|
||||||
import os.log
|
import os.log
|
||||||
import Path
|
import Path
|
||||||
|
import XcodesKit
|
||||||
public typealias ProcessOutput = (status: Int32, out: String, err: String)
|
|
||||||
|
|
||||||
extension Process {
|
extension Process {
|
||||||
@discardableResult
|
@discardableResult
|
||||||
static func run(_ executable: Path, workingDirectory: URL? = nil, input: String? = nil, _ arguments: String...) -> AnyPublisher<ProcessOutput, Error> {
|
static func run(_ executable: any Pathish, workingDirectory: URL? = nil, input: String? = nil, _ arguments: String...) -> AnyPublisher<ProcessOutput, Error> {
|
||||||
return run(executable.url, workingDirectory: workingDirectory, input: input, arguments)
|
return run(executable.url, workingDirectory: workingDirectory, input: input, arguments)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -67,9 +66,3 @@ extension Process {
|
||||||
.eraseToAnyPublisher()
|
.eraseToAnyPublisher()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
struct ProcessExecutionError: Error {
|
|
||||||
let process: Process
|
|
||||||
let standardOutput: String
|
|
||||||
let standardError: String
|
|
||||||
}
|
|
||||||
|
|
|
||||||
32
Xcodes/Backend/SDKs+Xcode.swift
Normal file
32
Xcodes/Backend/SDKs+Xcode.swift
Normal file
|
|
@ -0,0 +1,32 @@
|
||||||
|
//
|
||||||
|
// SDKs+Xcode.swift
|
||||||
|
// Xcodes
|
||||||
|
//
|
||||||
|
// Created by Matt Kiazyk on 2023-06-05.
|
||||||
|
// Copyright © 2023 Robots and Pencils. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
import Foundation
|
||||||
|
import struct XCModel.SDKs
|
||||||
|
|
||||||
|
extension SDKs {
|
||||||
|
/// Loops through all SDK's and returns an array of buildNumbers (to be used to correlate runtimes)
|
||||||
|
func allBuilds() -> [String] {
|
||||||
|
var buildNumbers: [String] = []
|
||||||
|
|
||||||
|
if let iOS = self.iOS?.compactMap({ $0.build }) {
|
||||||
|
buildNumbers += iOS
|
||||||
|
}
|
||||||
|
if let tvOS = self.tvOS?.compactMap({ $0.build }) {
|
||||||
|
buildNumbers += tvOS
|
||||||
|
}
|
||||||
|
if let macOS = self.macOS?.compactMap({ $0.build }) {
|
||||||
|
buildNumbers += macOS
|
||||||
|
}
|
||||||
|
if let watchOS = self.watchOS?.compactMap({ $0.build }) {
|
||||||
|
buildNumbers += watchOS
|
||||||
|
}
|
||||||
|
|
||||||
|
return buildNumbers
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -67,4 +67,5 @@ struct Xcode: Identifiable, CustomStringConvertible {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
import SwiftUI
|
import SwiftUI
|
||||||
|
import XcodesKit
|
||||||
|
|
||||||
// MARK: - CommandMenu
|
// MARK: - CommandMenu
|
||||||
|
|
||||||
|
|
@ -207,6 +208,23 @@ struct CreateSymbolicLinkButton: View {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct DownloadRuntimeButton: View {
|
||||||
|
@EnvironmentObject var appState: AppState
|
||||||
|
let runtime: DownloadableRuntime?
|
||||||
|
|
||||||
|
var body: some View {
|
||||||
|
Button(action: install) {
|
||||||
|
Text("Install")
|
||||||
|
.help("Install")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private func install() {
|
||||||
|
guard let runtime = runtime else { return }
|
||||||
|
// appState.checkMinVersionAndInstall(id: xcode.id)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
struct CreateSymbolicBetaLinkButton: View {
|
struct CreateSymbolicBetaLinkButton: View {
|
||||||
@EnvironmentObject var appState: AppState
|
@EnvironmentObject var appState: AppState
|
||||||
let xcode: Xcode?
|
let xcode: Xcode?
|
||||||
|
|
|
||||||
|
|
@ -54,6 +54,7 @@ struct InfoPane: View {
|
||||||
Divider()
|
Divider()
|
||||||
|
|
||||||
Group{
|
Group{
|
||||||
|
runtimes(for: xcode)
|
||||||
releaseNotes(for: xcode)
|
releaseNotes(for: xcode)
|
||||||
releaseDate(for: xcode)
|
releaseDate(for: xcode)
|
||||||
identicalBuilds(for: xcode)
|
identicalBuilds(for: xcode)
|
||||||
|
|
@ -245,6 +246,29 @@ struct InfoPane: View {
|
||||||
.frame(maxWidth: .infinity, maxHeight: .infinity)
|
.frame(maxWidth: .infinity, maxHeight: .infinity)
|
||||||
.padding()
|
.padding()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ViewBuilder
|
||||||
|
private func runtimes(for xcode: Xcode) -> some View {
|
||||||
|
let runtimes = appState.getRunTimes(xcode: xcode)
|
||||||
|
|
||||||
|
VStack(alignment: .leading) {
|
||||||
|
Text("Platforms")
|
||||||
|
.font(.headline)
|
||||||
|
.frame(maxWidth: .infinity, alignment: .leading)
|
||||||
|
|
||||||
|
ForEach(runtimes, id: \.simulatorVersion.buildUpdate) { runtime in
|
||||||
|
HStack {
|
||||||
|
Text("\(runtime.visibleIdentifier)")
|
||||||
|
.font(.subheadline)
|
||||||
|
Spacer()
|
||||||
|
Text(runtime.downloadFileSizeString)
|
||||||
|
.font(.subheadline)
|
||||||
|
DownloadRuntimeButton(runtime: runtime)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
struct InfoPane_Previews: PreviewProvider {
|
struct InfoPane_Previews: PreviewProvider {
|
||||||
|
|
@ -329,6 +353,7 @@ struct InfoPane_Previews: PreviewProvider {
|
||||||
),
|
),
|
||||||
downloadFileSize: 242342424)
|
downloadFileSize: 242342424)
|
||||||
]
|
]
|
||||||
|
|
||||||
})
|
})
|
||||||
.previewDisplayName("Populated, Uninstalled")
|
.previewDisplayName("Populated, Uninstalled")
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,37 @@
|
||||||
{\rtf1\ansi\ansicpg1252\cocoartf2639
|
{\rtf1\ansi\ansicpg1252\cocoartf2709
|
||||||
\cocoatextscaling0\cocoaplatform0{\fonttbl\f0\fnil\fcharset0 .SFNS-Regular;}
|
\cocoatextscaling0\cocoaplatform0{\fonttbl\f0\fnil\fcharset0 .SFNS-Regular;}
|
||||||
{\colortbl;\red255\green255\blue255;}
|
{\colortbl;\red255\green255\blue255;}
|
||||||
{\*\expandedcolortbl;;}
|
{\*\expandedcolortbl;;}
|
||||||
\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardirnatural\partightenfactor0
|
\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardirnatural\partightenfactor0
|
||||||
|
|
||||||
\f0\fs34 \cf0 SwiftSoup\
|
\f0\fs34 \cf0 AsyncHTTPNetworkService\
|
||||||
|
\
|
||||||
|
|
||||||
|
\fs26 MIT License\
|
||||||
|
\
|
||||||
|
Copyright (c) 2022 Robots and Pencils\
|
||||||
|
\
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy\
|
||||||
|
of this software and associated documentation files (the "Software"), to deal\
|
||||||
|
in the Software without restriction, including without limitation the rights\
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\
|
||||||
|
copies of the Software, and to permit persons to whom the Software is\
|
||||||
|
furnished to do so, subject to the following conditions:\
|
||||||
|
\
|
||||||
|
The above copyright notice and this permission notice shall be included in all\
|
||||||
|
copies or substantial portions of the Software.\
|
||||||
|
\
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\
|
||||||
|
SOFTWARE.\
|
||||||
|
\
|
||||||
|
\
|
||||||
|
|
||||||
|
\fs34 SwiftSoup\
|
||||||
\
|
\
|
||||||
|
|
||||||
\fs26 MIT License\
|
\fs26 MIT License\
|
||||||
|
|
|
||||||
|
|
@ -40,6 +40,7 @@
|
||||||
"Compilers" = "Compilers";
|
"Compilers" = "Compilers";
|
||||||
"DownloadSize" = "Download Size";
|
"DownloadSize" = "Download Size";
|
||||||
"NoXcodeSelected" = "No Xcode Selected";
|
"NoXcodeSelected" = "No Xcode Selected";
|
||||||
|
"Platforms" = "Platforms";
|
||||||
|
|
||||||
// Installation Steps
|
// Installation Steps
|
||||||
// When localizing. Items will be replaced in order. ie "Step 1 of 6: Downloading"
|
// When localizing. Items will be replaced in order. ie "Step 1 of 6: Downloading"
|
||||||
|
|
|
||||||
9
Xcodes/XcodesKit/.gitignore
vendored
Normal file
9
Xcodes/XcodesKit/.gitignore
vendored
Normal file
|
|
@ -0,0 +1,9 @@
|
||||||
|
.DS_Store
|
||||||
|
/.build
|
||||||
|
/Packages
|
||||||
|
/*.xcodeproj
|
||||||
|
xcuserdata/
|
||||||
|
DerivedData/
|
||||||
|
.swiftpm/config/registries.json
|
||||||
|
.swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata
|
||||||
|
.netrc
|
||||||
33
Xcodes/XcodesKit/Package.swift
Normal file
33
Xcodes/XcodesKit/Package.swift
Normal file
|
|
@ -0,0 +1,33 @@
|
||||||
|
// swift-tools-version: 5.7
|
||||||
|
// The swift-tools-version declares the minimum version of Swift required to build this package.
|
||||||
|
|
||||||
|
import PackageDescription
|
||||||
|
|
||||||
|
let package = Package(
|
||||||
|
name: "XcodesKit",
|
||||||
|
platforms: [.macOS(.v11)],
|
||||||
|
products: [
|
||||||
|
// Products define the executables and libraries a package produces, and make them visible to other packages.
|
||||||
|
.library(
|
||||||
|
name: "XcodesKit",
|
||||||
|
targets: ["XcodesKit"]),
|
||||||
|
],
|
||||||
|
dependencies: [
|
||||||
|
// Dependencies declare other packages that this package depends on.
|
||||||
|
.package(url: "https://github.com/RobotsAndPencils/AsyncHTTPNetworkService", branch: "main"),
|
||||||
|
.package(url: "https://github.com/mxcl/Path.swift", from: "1.0.0"),
|
||||||
|
],
|
||||||
|
targets: [
|
||||||
|
// Targets are the basic building blocks of a package. A target can define a module or a test suite.
|
||||||
|
// Targets can depend on other targets in this package, and on products in packages this package depends on.
|
||||||
|
.target(
|
||||||
|
name: "XcodesKit",
|
||||||
|
dependencies: [
|
||||||
|
.product(name: "AsyncNetworkService", package: "AsyncHTTPNetworkService"),
|
||||||
|
.product(name: "Path", package: "Path.swift")
|
||||||
|
]),
|
||||||
|
.testTarget(
|
||||||
|
name: "XcodesKitTests",
|
||||||
|
dependencies: ["XcodesKit"]),
|
||||||
|
]
|
||||||
|
)
|
||||||
3
Xcodes/XcodesKit/README.md
Normal file
3
Xcodes/XcodesKit/README.md
Normal file
|
|
@ -0,0 +1,3 @@
|
||||||
|
# XcodesKit
|
||||||
|
|
||||||
|
A description of this package.
|
||||||
7
Xcodes/XcodesKit/Sources/XcodesKit/Environment.swift
Normal file
7
Xcodes/XcodesKit/Sources/XcodesKit/Environment.swift
Normal file
|
|
@ -0,0 +1,7 @@
|
||||||
|
import Foundation
|
||||||
|
|
||||||
|
public struct Environment {
|
||||||
|
public var shell = Shell()
|
||||||
|
}
|
||||||
|
|
||||||
|
public var Current = Environment()
|
||||||
|
|
@ -0,0 +1,12 @@
|
||||||
|
import Foundation
|
||||||
|
|
||||||
|
extension NSRegularExpression {
|
||||||
|
func firstString(in string: String, options: NSRegularExpression.MatchingOptions = []) -> String? {
|
||||||
|
let range = NSRange(location: 0, length: string.utf16.count)
|
||||||
|
guard let firstMatch = firstMatch(in: string, options: options, range: range),
|
||||||
|
let resultRange = Range(firstMatch.range, in: string) else {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return String(string[resultRange])
|
||||||
|
}
|
||||||
|
}
|
||||||
10
Xcodes/XcodesKit/Sources/XcodesKit/Extensions/Logger.swift
Normal file
10
Xcodes/XcodesKit/Sources/XcodesKit/Extensions/Logger.swift
Normal file
|
|
@ -0,0 +1,10 @@
|
||||||
|
import Foundation
|
||||||
|
import os.log
|
||||||
|
|
||||||
|
extension Logger {
|
||||||
|
private static var subsystem = Bundle.main.bundleIdentifier!
|
||||||
|
|
||||||
|
static public let appState = Logger(subsystem: subsystem, category: "appState")
|
||||||
|
static public let helperClient = Logger(subsystem: subsystem, category: "helperClient")
|
||||||
|
static public let subprocess = Logger(subsystem: subsystem, category: "subprocess")
|
||||||
|
}
|
||||||
34
Xcodes/XcodesKit/Sources/XcodesKit/Models/InstallState.swift
Normal file
34
Xcodes/XcodesKit/Sources/XcodesKit/Models/InstallState.swift
Normal file
|
|
@ -0,0 +1,34 @@
|
||||||
|
//
|
||||||
|
// InstallState.swift
|
||||||
|
//
|
||||||
|
//
|
||||||
|
// Created by Matt Kiazyk on 2023-06-06.
|
||||||
|
//
|
||||||
|
|
||||||
|
import Foundation
|
||||||
|
import Path
|
||||||
|
|
||||||
|
public enum InstallState: Equatable {
|
||||||
|
case notInstalled
|
||||||
|
case installing(InstallationStep)
|
||||||
|
case installed(Path)
|
||||||
|
|
||||||
|
var notInstalled: Bool {
|
||||||
|
switch self {
|
||||||
|
case .notInstalled: return true
|
||||||
|
default: return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
var installing: Bool {
|
||||||
|
switch self {
|
||||||
|
case .installing: return true
|
||||||
|
default: return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
var installed: Bool {
|
||||||
|
switch self {
|
||||||
|
case .installed: return true
|
||||||
|
default: return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,60 @@
|
||||||
|
//
|
||||||
|
// InstallationStep.swift
|
||||||
|
//
|
||||||
|
//
|
||||||
|
// Created by Matt Kiazyk on 2023-06-06.
|
||||||
|
//
|
||||||
|
|
||||||
|
import Foundation
|
||||||
|
|
||||||
|
// A numbered step
|
||||||
|
public enum InstallationStep: Equatable, CustomStringConvertible {
|
||||||
|
case downloading(progress: Progress)
|
||||||
|
case unarchiving
|
||||||
|
case moving(destination: String)
|
||||||
|
case trashingArchive
|
||||||
|
case checkingSecurity
|
||||||
|
case finishing
|
||||||
|
|
||||||
|
public var description: String {
|
||||||
|
"(\(stepNumber)/\(stepCount)) \(message)"
|
||||||
|
}
|
||||||
|
|
||||||
|
var message: String {
|
||||||
|
switch self {
|
||||||
|
case .downloading:
|
||||||
|
return localizeString("Downloading")
|
||||||
|
case .unarchiving:
|
||||||
|
return localizeString("Unarchiving")
|
||||||
|
case .moving(let destination):
|
||||||
|
return String(format: localizeString("Moving"), destination)
|
||||||
|
case .trashingArchive:
|
||||||
|
return localizeString("TrashingArchive")
|
||||||
|
case .checkingSecurity:
|
||||||
|
return localizeString("CheckingSecurity")
|
||||||
|
case .finishing:
|
||||||
|
return localizeString("Finishing")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var stepNumber: Int {
|
||||||
|
switch self {
|
||||||
|
case .downloading: return 1
|
||||||
|
case .unarchiving: return 2
|
||||||
|
case .moving: return 3
|
||||||
|
case .trashingArchive: return 4
|
||||||
|
case .checkingSecurity: return 5
|
||||||
|
case .finishing: return 6
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var stepCount: Int { 6 }
|
||||||
|
}
|
||||||
|
func localizeString(_ key: String, comment: String = "") -> String {
|
||||||
|
if #available(macOS 12, *) {
|
||||||
|
return String(localized: String.LocalizationValue(key))
|
||||||
|
} else {
|
||||||
|
return NSLocalizedString(key, comment: comment)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,21 @@
|
||||||
|
//
|
||||||
|
// CoreSimulatorImage.swift
|
||||||
|
//
|
||||||
|
//
|
||||||
|
// Created by Matt Kiazyk on 2023-01-08.
|
||||||
|
//
|
||||||
|
|
||||||
|
import Foundation
|
||||||
|
|
||||||
|
public struct CoreSimulatorPlist: Decodable {
|
||||||
|
public let images: [CoreSimulatorImage]
|
||||||
|
}
|
||||||
|
|
||||||
|
public struct CoreSimulatorImage: Decodable {
|
||||||
|
public let uuid: String
|
||||||
|
public let runtimeInfo: CoreSimulatorRuntimeInfo
|
||||||
|
}
|
||||||
|
|
||||||
|
public struct CoreSimulatorRuntimeInfo: Decodable {
|
||||||
|
public let build: String
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,161 @@
|
||||||
|
import Foundation
|
||||||
|
|
||||||
|
public struct DownloadableRuntimesResponse: Codable {
|
||||||
|
public let sdkToSimulatorMappings: [SDKToSimulatorMapping]
|
||||||
|
public let sdkToSeedMappings: [SDKToSeedMapping]
|
||||||
|
public let refreshInterval: Int
|
||||||
|
public let downloadables: [DownloadableRuntime]
|
||||||
|
public let version: String
|
||||||
|
}
|
||||||
|
|
||||||
|
public struct DownloadableRuntime: Codable {
|
||||||
|
public let category: Category
|
||||||
|
public let simulatorVersion: SimulatorVersion
|
||||||
|
public let source: String
|
||||||
|
public let dictionaryVersion: Int
|
||||||
|
public let contentType: ContentType
|
||||||
|
public let platform: Platform
|
||||||
|
public let identifier: String
|
||||||
|
public let version: String
|
||||||
|
public let fileSize: Int
|
||||||
|
public let hostRequirements: HostRequirements?
|
||||||
|
public let name: String
|
||||||
|
public let authentication: Authentication?
|
||||||
|
|
||||||
|
// dynamically updated - not decoded
|
||||||
|
public var installState: InstallState = .notInstalled
|
||||||
|
|
||||||
|
enum CodingKeys: CodingKey {
|
||||||
|
case category
|
||||||
|
case simulatorVersion
|
||||||
|
case source
|
||||||
|
case dictionaryVersion
|
||||||
|
case contentType
|
||||||
|
case platform
|
||||||
|
case identifier
|
||||||
|
case version
|
||||||
|
case fileSize
|
||||||
|
case hostRequirements
|
||||||
|
case name
|
||||||
|
case authentication
|
||||||
|
}
|
||||||
|
|
||||||
|
var betaNumber: Int? {
|
||||||
|
enum Regex { static let shared = try! NSRegularExpression(pattern: "b[0-9]+$") }
|
||||||
|
guard var foundString = Regex.shared.firstString(in: identifier) else { return nil }
|
||||||
|
foundString.removeFirst()
|
||||||
|
return Int(foundString)!
|
||||||
|
}
|
||||||
|
|
||||||
|
var completeVersion: String {
|
||||||
|
makeVersion(for: simulatorVersion.version, betaNumber: betaNumber)
|
||||||
|
}
|
||||||
|
|
||||||
|
public var visibleIdentifier: String {
|
||||||
|
return platform.shortName + " " + completeVersion
|
||||||
|
}
|
||||||
|
|
||||||
|
func makeVersion(for osVersion: String, betaNumber: Int?) -> String {
|
||||||
|
let betaSuffix = betaNumber.flatMap { "-beta\($0)" } ?? ""
|
||||||
|
return osVersion + betaSuffix
|
||||||
|
}
|
||||||
|
|
||||||
|
public var downloadFileSizeString: String {
|
||||||
|
return ByteCountFormatter.string(fromByteCount: Int64(fileSize), countStyle: .file)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public struct SDKToSeedMapping: Codable {
|
||||||
|
public let buildUpdate: String
|
||||||
|
public let platform: DownloadableRuntime.Platform
|
||||||
|
public let seedNumber: Int
|
||||||
|
}
|
||||||
|
|
||||||
|
public struct SDKToSimulatorMapping: Codable {
|
||||||
|
public let sdkBuildUpdate: String
|
||||||
|
public let simulatorBuildUpdate: String
|
||||||
|
public let sdkIdentifier: String
|
||||||
|
}
|
||||||
|
|
||||||
|
extension DownloadableRuntime {
|
||||||
|
public struct SimulatorVersion: Codable {
|
||||||
|
public let buildUpdate: String
|
||||||
|
public let version: String
|
||||||
|
}
|
||||||
|
|
||||||
|
public struct HostRequirements: Codable {
|
||||||
|
let maxHostVersion: String?
|
||||||
|
let excludedHostArchitectures: [String]?
|
||||||
|
let minHostVersion: String?
|
||||||
|
let minXcodeVersion: String?
|
||||||
|
}
|
||||||
|
|
||||||
|
public enum Authentication: String, Codable {
|
||||||
|
case virtual = "virtual"
|
||||||
|
}
|
||||||
|
|
||||||
|
public enum Category: String, Codable {
|
||||||
|
case simulator = "simulator"
|
||||||
|
}
|
||||||
|
|
||||||
|
public enum ContentType: String, Codable {
|
||||||
|
case diskImage = "diskImage"
|
||||||
|
case package = "package"
|
||||||
|
}
|
||||||
|
|
||||||
|
public enum Platform: String, Codable {
|
||||||
|
case iOS = "com.apple.platform.iphoneos"
|
||||||
|
case macOS = "com.apple.platform.macosx"
|
||||||
|
case watchOS = "com.apple.platform.watchos"
|
||||||
|
case tvOS = "com.apple.platform.appletvos"
|
||||||
|
|
||||||
|
var order: Int {
|
||||||
|
switch self {
|
||||||
|
case .iOS: return 1
|
||||||
|
case .macOS: return 2
|
||||||
|
case .watchOS: return 3
|
||||||
|
case .tvOS: return 4
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var shortName: String {
|
||||||
|
switch self {
|
||||||
|
case .iOS: return "iOS"
|
||||||
|
case .macOS: return "macOS"
|
||||||
|
case .watchOS: return "watchOS"
|
||||||
|
case .tvOS: return "tvOS"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public struct InstalledRuntime: Decodable {
|
||||||
|
let build: String
|
||||||
|
let deletable: Bool
|
||||||
|
let identifier: UUID
|
||||||
|
let kind: Kind
|
||||||
|
let lastUsedAt: Date?
|
||||||
|
let path: String
|
||||||
|
let platformIdentifier: Platform
|
||||||
|
let runtimeBundlePath: String
|
||||||
|
let runtimeIdentifier: String
|
||||||
|
let signatureState: String
|
||||||
|
let state: String
|
||||||
|
let version: String
|
||||||
|
let sizeBytes: Int?
|
||||||
|
}
|
||||||
|
|
||||||
|
extension InstalledRuntime {
|
||||||
|
enum Kind: String, Decodable {
|
||||||
|
case diskImage = "Disk Image"
|
||||||
|
case bundled = "Bundled with Xcode"
|
||||||
|
case legacyDownload = "Legacy Download"
|
||||||
|
}
|
||||||
|
|
||||||
|
enum Platform: String, Decodable {
|
||||||
|
case tvOS = "com.apple.platform.appletvsimulator"
|
||||||
|
case iOS = "com.apple.platform.iphonesimulator"
|
||||||
|
case watchOS = "com.apple.platform.watchsimulator"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
@ -0,0 +1,55 @@
|
||||||
|
import Foundation
|
||||||
|
import AsyncNetworkService
|
||||||
|
import Path
|
||||||
|
|
||||||
|
extension URL {
|
||||||
|
static let downloadableRuntimes = URL(string: "https://devimages-cdn.apple.com/downloads/xcode/simulators/index2.dvtdownloadableindex")!
|
||||||
|
}
|
||||||
|
|
||||||
|
public struct RuntimeService {
|
||||||
|
var networkService: AsyncHTTPNetworkService
|
||||||
|
|
||||||
|
public init() {
|
||||||
|
networkService = AsyncHTTPNetworkService()
|
||||||
|
}
|
||||||
|
|
||||||
|
public func downloadableRuntimes() async throws -> DownloadableRuntimesResponse {
|
||||||
|
let urlRequest = URLRequest(url: .downloadableRuntimes)
|
||||||
|
|
||||||
|
// Apple gives a plist for download
|
||||||
|
let (data, _) = try await networkService.requestData(urlRequest, validators: [])
|
||||||
|
let decodedResponse = try PropertyListDecoder().decode(DownloadableRuntimesResponse.self, from: data)
|
||||||
|
|
||||||
|
return decodedResponse
|
||||||
|
}
|
||||||
|
|
||||||
|
public func installedRuntimes() async throws -> [InstalledRuntime] {
|
||||||
|
// This only uses the Selected Xcode, so we don't know what other SDK's have been installed in previous versions
|
||||||
|
let output = try await Current.shell.installedRuntimes()
|
||||||
|
|
||||||
|
let decoder = JSONDecoder()
|
||||||
|
decoder.dateDecodingStrategy = .iso8601
|
||||||
|
let outputDictionary = try decoder.decode([String: InstalledRuntime].self, from: output.out.data(using: .utf8)!)
|
||||||
|
|
||||||
|
return outputDictionary.values.sorted { first, second in
|
||||||
|
return first.identifier.uuidString.compare(second.identifier.uuidString, options: .numeric) == .orderedAscending
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Loops through `/Library/Developer/CoreSimulator/images/images.plist` which contains a list of downloaded Simuator Runtimes
|
||||||
|
/// This is different then using `simctl` (`installedRuntimes()`) which only returns the installed runtimes for the selected xcode version.
|
||||||
|
public func localInstalledRuntimes() async throws -> [CoreSimulatorRuntimeInfo] {
|
||||||
|
guard let path = Path("/Library/Developer/CoreSimulator/images/images.plist") else { throw "Could not find images.plist for CoreSimulators" }
|
||||||
|
guard let infoPlistData = FileManager.default.contents(atPath: path.string) else { throw "Could not get data from \(path.string)" }
|
||||||
|
|
||||||
|
do {
|
||||||
|
let infoPlist: CoreSimulatorPlist = try PropertyListDecoder().decode(CoreSimulatorPlist.self, from: infoPlistData)
|
||||||
|
return infoPlist.images.map { $0.runtimeInfo }
|
||||||
|
} catch {
|
||||||
|
throw error
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
extension String: Error {}
|
||||||
66
Xcodes/XcodesKit/Sources/XcodesKit/Shell/Process.swift
Normal file
66
Xcodes/XcodesKit/Sources/XcodesKit/Shell/Process.swift
Normal file
|
|
@ -0,0 +1,66 @@
|
||||||
|
import Foundation
|
||||||
|
import Path
|
||||||
|
import os.log
|
||||||
|
|
||||||
|
public typealias ProcessOutput = (status: Int32, out: String, err: String)
|
||||||
|
|
||||||
|
extension Process {
|
||||||
|
static func run(_ executable: Path, workingDirectory: URL? = nil, input: String? = nil, _ arguments: String...) async throws -> ProcessOutput {
|
||||||
|
return try await run(executable.url, workingDirectory: workingDirectory, input: input, arguments)
|
||||||
|
}
|
||||||
|
|
||||||
|
static func run(_ executable: URL, workingDirectory: URL? = nil, input: String? = nil, _ arguments: [String]) async throws -> ProcessOutput {
|
||||||
|
|
||||||
|
let process = Process()
|
||||||
|
process.currentDirectoryURL = workingDirectory ?? executable.deletingLastPathComponent()
|
||||||
|
process.executableURL = executable
|
||||||
|
process.arguments = arguments
|
||||||
|
|
||||||
|
let (stdout, stderr) = (Pipe(), Pipe())
|
||||||
|
process.standardOutput = stdout
|
||||||
|
process.standardError = stderr
|
||||||
|
|
||||||
|
if let input = input {
|
||||||
|
let inputPipe = Pipe()
|
||||||
|
process.standardInput = inputPipe.fileHandleForReading
|
||||||
|
inputPipe.fileHandleForWriting.write(Data(input.utf8))
|
||||||
|
inputPipe.fileHandleForWriting.closeFile()
|
||||||
|
}
|
||||||
|
|
||||||
|
do {
|
||||||
|
Logger.subprocess.info("Process.run executable: \(executable), input: \(input ?? ""), arguments: \(arguments.joined(separator: ", "))")
|
||||||
|
|
||||||
|
try process.run()
|
||||||
|
process.waitUntilExit()
|
||||||
|
|
||||||
|
let output = String(data: stdout.fileHandleForReading.readDataToEndOfFile(), encoding: .utf8) ?? ""
|
||||||
|
let error = String(data: stderr.fileHandleForReading.readDataToEndOfFile(), encoding: .utf8) ?? ""
|
||||||
|
|
||||||
|
Logger.subprocess.info("Process.run output: \(output)")
|
||||||
|
if !error.isEmpty {
|
||||||
|
Logger.subprocess.error("Process.run error: \(error)")
|
||||||
|
}
|
||||||
|
|
||||||
|
guard process.terminationReason == .exit, process.terminationStatus == 0 else {
|
||||||
|
throw ProcessExecutionError(process: process, standardOutput: output, standardError: error)
|
||||||
|
}
|
||||||
|
|
||||||
|
return (process.terminationStatus, output, error)
|
||||||
|
} catch {
|
||||||
|
throw error
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public struct ProcessExecutionError: Error {
|
||||||
|
public let process: Process
|
||||||
|
public let standardOutput: String
|
||||||
|
public let standardError: String
|
||||||
|
|
||||||
|
public init(process: Process, standardOutput: String, standardError: String) {
|
||||||
|
self.process = process
|
||||||
|
self.standardOutput = standardOutput
|
||||||
|
self.standardError = standardError
|
||||||
|
}
|
||||||
|
}
|
||||||
8
Xcodes/XcodesKit/Sources/XcodesKit/Shell/Shell.swift
Normal file
8
Xcodes/XcodesKit/Sources/XcodesKit/Shell/Shell.swift
Normal file
|
|
@ -0,0 +1,8 @@
|
||||||
|
import Foundation
|
||||||
|
import Path
|
||||||
|
|
||||||
|
public struct Shell {
|
||||||
|
public var installedRuntimes: () async throws -> ProcessOutput = {
|
||||||
|
try await Process.run(Path.root.usr.bin.join("xcrun"), "simctl", "runtime", "list", "-j")
|
||||||
|
}
|
||||||
|
}
|
||||||
11
Xcodes/XcodesKit/Tests/XcodesKitTests/XcodesKitTests.swift
Normal file
11
Xcodes/XcodesKit/Tests/XcodesKitTests/XcodesKitTests.swift
Normal file
|
|
@ -0,0 +1,11 @@
|
||||||
|
import XCTest
|
||||||
|
@testable import XcodesKit
|
||||||
|
|
||||||
|
final class XcodesKitTests: XCTestCase {
|
||||||
|
func testExample() throws {
|
||||||
|
// This is an example of a functional test case.
|
||||||
|
// Use XCTAssert and related functions to verify your tests produce the correct
|
||||||
|
// results.
|
||||||
|
XCTAssertEqual(XcodesKit().text, "Hello, World!")
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
Reference in a new issue