mirror of
https://github.com/XcodesOrg/XcodesApp.git
synced 2026-03-25 08:55:46 +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 */; };
|
||||
CA378F992466567600A58CE0 /* AppState.swift in Sources */ = {isa = PBXBuildFile; fileRef = CA378F982466567600A58CE0 /* AppState.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 */; };
|
||||
CA44901F2463AD34003D8213 /* Tag.swift in Sources */ = {isa = PBXBuildFile; fileRef = CA44901E2463AD34003D8213 /* Tag.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 */; };
|
||||
CABFA9CE2592EEEA00380FEE /* Version+Xcode.swift in Sources */ = {isa = PBXBuildFile; fileRef = CABFA9A62592EEE900380FEE /* Version+Xcode.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 */; };
|
||||
CABFA9EE2592F0CC00380FEE /* SwiftSoup in Frameworks */ = {isa = PBXBuildFile; productRef = CABFA9ED2592F0CC00380FEE /* SwiftSoup */; };
|
||||
CABFA9F82592F0F900380FEE /* KeychainAccess in Frameworks */ = {isa = PBXBuildFile; productRef = CABFA9F72592F0F900380FEE /* KeychainAccess */; };
|
||||
|
|
@ -108,6 +106,9 @@
|
|||
E87DD6EB25D053FA00D86808 /* Progress+.swift in Sources */ = {isa = PBXBuildFile; fileRef = E87DD6EA25D053FA00D86808 /* Progress+.swift */; };
|
||||
E89342FA25EDCC17007CF557 /* NotificationManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = E89342F925EDCC17007CF557 /* NotificationManager.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, ); }; };
|
||||
E8CBDB8B27AE02FF00B22292 /* ExperiementsPreferencePane.swift in Sources */ = {isa = PBXBuildFile; fileRef = E8CBDB8A27AE02FF00B22292 /* ExperiementsPreferencePane.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 */; };
|
||||
E8E98A9025D8631800EC89A0 /* InstallationStepRowView.swift in Sources */ = {isa = PBXBuildFile; fileRef = CAFBC3FF259AC17F00E2A3D8 /* InstallationStepRowView.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 */; };
|
||||
E8FD5727291EE4AC001E004C /* AsyncNetworkService in Frameworks */ = {isa = PBXBuildFile; productRef = E8FD5726291EE4AC001E004C /* AsyncNetworkService */; };
|
||||
/* End PBXBuildFile section */
|
||||
|
||||
/* Begin PBXContainerItemProxy section */
|
||||
|
|
@ -196,7 +199,6 @@
|
|||
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>"; };
|
||||
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>"; };
|
||||
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>"; };
|
||||
|
|
@ -297,11 +299,14 @@
|
|||
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>"; };
|
||||
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>"; };
|
||||
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>"; };
|
||||
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>"; };
|
||||
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>"; };
|
||||
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>"; };
|
||||
|
|
@ -328,9 +333,11 @@
|
|||
CA9FF86D25951C6E00E47BAF /* XCModel in Frameworks */,
|
||||
CABFA9F82592F0F900380FEE /* KeychainAccess in Frameworks */,
|
||||
CAA858CD25A3D8BC00ACF8C0 /* ErrorHandling in Frameworks */,
|
||||
E8C0EB1A291EF43E0081528A /* XcodesKit in Frameworks */,
|
||||
E8FD5727291EE4AC001E004C /* AsyncNetworkService in Frameworks */,
|
||||
CAA1CB2D255A5262003FD669 /* AppleAPI in Frameworks */,
|
||||
CABFA9DF2592F07A00380FEE /* Path in Frameworks */,
|
||||
CABFA9EE2592F0CC00380FEE /* SwiftSoup in Frameworks */,
|
||||
E8F44A1E296B4CD7002D6592 /* Path in Frameworks */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
|
|
@ -452,6 +459,7 @@
|
|||
isa = PBXGroup;
|
||||
children = (
|
||||
CA378F982466567600A58CE0 /* AppState.swift */,
|
||||
E8C0EB1B291EF9A10081528A /* AppState+Runtimes.swift */,
|
||||
CAE424B3259A764700B8B246 /* AppState+Install.swift */,
|
||||
CABFA9A72592EEE900380FEE /* AppState+Update.swift */,
|
||||
CAA8589A25A2B83000ACF8C0 /* Aria2CError.swift */,
|
||||
|
|
@ -473,7 +481,6 @@
|
|||
CAC281D9259F985100B8AB0B /* InstallationStep.swift */,
|
||||
CA9FF8862595607900E47BAF /* InstalledXcode.swift */,
|
||||
CAA8587B25A2B37900ACF8C0 /* IsTesting.swift */,
|
||||
CA42DD6D25AEA8B200BC0B0C /* Logger.swift */,
|
||||
E89342F925EDCC17007CF557 /* NotificationManager.swift */,
|
||||
CAE4248B259A68B800B8B246 /* Optional+IsNotNil.swift */,
|
||||
CABFA9AE2592EEE900380FEE /* Path+.swift */,
|
||||
|
|
@ -492,6 +499,7 @@
|
|||
E81D7E9F2805250100A205FC /* Collection+.swift */,
|
||||
E8D655BF288DD04700A139C2 /* SelectedActionType.swift */,
|
||||
E87AB3C42939B65E00D72F43 /* Hardware.swift */,
|
||||
E8B20CBE2A2EDEC20057D816 /* SDKs+Xcode.swift */,
|
||||
);
|
||||
path = Backend;
|
||||
sourceTree = "<group>";
|
||||
|
|
@ -532,6 +540,7 @@
|
|||
CAD2E7952449574E00113D76 = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
E856BB73291EDD3D00DC438B /* XcodesKit */,
|
||||
CA8FB5F8256E0F9400469DA5 /* README.md */,
|
||||
CABFA9D42592EF6300380FEE /* DECISIONS.md */,
|
||||
CABFA9A02592EAF500380FEE /* R&PLogo.png */,
|
||||
|
|
@ -656,7 +665,6 @@
|
|||
name = Xcodes;
|
||||
packageProductDependencies = (
|
||||
CAA1CB2C255A5262003FD669 /* AppleAPI */,
|
||||
CABFA9DE2592F07A00380FEE /* Path */,
|
||||
CABFA9E32592F08E00380FEE /* Version */,
|
||||
CABFA9ED2592F0CC00380FEE /* SwiftSoup */,
|
||||
CABFA9F72592F0F900380FEE /* KeychainAccess */,
|
||||
|
|
@ -664,6 +672,9 @@
|
|||
CA9FF86C25951C6E00E47BAF /* XCModel */,
|
||||
CAA858CC25A3D8BC00ACF8C0 /* ErrorHandling */,
|
||||
E8F81FC3282D8A17006CBD0F /* Sparkle */,
|
||||
E8FD5726291EE4AC001E004C /* AsyncNetworkService */,
|
||||
E8C0EB19291EF43E0081528A /* XcodesKit */,
|
||||
E8F44A1D296B4CD7002D6592 /* Path */,
|
||||
);
|
||||
productName = XcodesMac;
|
||||
productReference = CAD2E79E2449574E00113D76 /* Xcodes.app */;
|
||||
|
|
@ -737,7 +748,6 @@
|
|||
);
|
||||
mainGroup = CAD2E7952449574E00113D76;
|
||||
packageReferences = (
|
||||
CABFA9DD2592F07A00380FEE /* XCRemoteSwiftPackageReference "Path" */,
|
||||
CABFA9E22592F08E00380FEE /* XCRemoteSwiftPackageReference "Version" */,
|
||||
CABFA9EC2592F0CC00380FEE /* XCRemoteSwiftPackageReference "SwiftSoup" */,
|
||||
CABFA9F62592F0F900380FEE /* XCRemoteSwiftPackageReference "KeychainAccess" */,
|
||||
|
|
@ -746,6 +756,8 @@
|
|||
CAA858CB25A3D8BC00ACF8C0 /* XCRemoteSwiftPackageReference "ErrorHandling" */,
|
||||
CAC28186259EE27200B8AB0B /* XCRemoteSwiftPackageReference "CombineExpectations" */,
|
||||
E8F81FC2282D8A17006CBD0F /* XCRemoteSwiftPackageReference "Sparkle" */,
|
||||
E8FD5725291EE4AC001E004C /* XCRemoteSwiftPackageReference "AsyncHTTPNetworkService" */,
|
||||
E8F44A1C296B4CD7002D6592 /* XCRemoteSwiftPackageReference "Path" */,
|
||||
);
|
||||
productRefGroup = CAD2E79F2449574E00113D76 /* Products */;
|
||||
projectDirPath = "";
|
||||
|
|
@ -854,7 +866,6 @@
|
|||
36741BFF291E50F500A85AAE /* FileError.swift in Sources */,
|
||||
CA9FF8872595607900E47BAF /* InstalledXcode.swift in Sources */,
|
||||
53CBAB2C263DCC9100410495 /* XcodesAlert.swift in Sources */,
|
||||
CA42DD6E25AEA8B200BC0B0C /* Logger.swift in Sources */,
|
||||
CA61A6E0259835580008926E /* Xcode.swift in Sources */,
|
||||
CAE4247F259A666100B8B246 /* MainWindow.swift in Sources */,
|
||||
CA452BB0259FD9770072DFA4 /* ProgressIndicator.swift in Sources */,
|
||||
|
|
@ -871,6 +882,7 @@
|
|||
E8DA461125FAF7FB002E85EF /* NotificationsView.swift in Sources */,
|
||||
CAA1CB35255A5AD5003FD669 /* SignInCredentialsView.swift in Sources */,
|
||||
E81D7EA02805250100A205FC /* Collection+.swift in Sources */,
|
||||
E8B20CBF2A2EDEC20057D816 /* SDKs+Xcode.swift in Sources */,
|
||||
CA9FF877259528CC00E47BAF /* Version+XcodeReleases.swift in Sources */,
|
||||
CABFAA2D2592FBFC00380FEE /* Configure.swift in Sources */,
|
||||
CA73510D257BFCEF00EA9CF8 /* NSAttributedString+.swift in Sources */,
|
||||
|
|
@ -894,6 +906,7 @@
|
|||
CABFA9C92592EEEA00380FEE /* URLRequest+Apple.swift in Sources */,
|
||||
CABFAA432593104F00380FEE /* AboutView.swift in Sources */,
|
||||
E8D0296F284B029800647641 /* BottomStatusBar.swift in Sources */,
|
||||
E8C0EB1C291EF9A10081528A /* AppState+Runtimes.swift in Sources */,
|
||||
E8E98A9025D8631800EC89A0 /* InstallationStepRowView.swift in Sources */,
|
||||
CABFA9CC2592EEEA00380FEE /* Path+.swift in Sources */,
|
||||
CAD2E7A22449574E00113D76 /* XcodesApp.swift in Sources */,
|
||||
|
|
@ -1416,14 +1429,6 @@
|
|||
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" */ = {
|
||||
isa = XCRemoteSwiftPackageReference;
|
||||
repositoryURL = "https://github.com/mxcl/Version";
|
||||
|
|
@ -1464,6 +1469,14 @@
|
|||
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" */ = {
|
||||
isa = XCRemoteSwiftPackageReference;
|
||||
repositoryURL = "https://github.com/sparkle-project/Sparkle";
|
||||
|
|
@ -1472,6 +1485,14 @@
|
|||
minimumVersion = 2.0.0;
|
||||
};
|
||||
};
|
||||
E8FD5725291EE4AC001E004C /* XCRemoteSwiftPackageReference "AsyncHTTPNetworkService" */ = {
|
||||
isa = XCRemoteSwiftPackageReference;
|
||||
repositoryURL = "https://github.com/RobotsAndPencils/AsyncHTTPNetworkService";
|
||||
requirement = {
|
||||
branch = main;
|
||||
kind = branch;
|
||||
};
|
||||
};
|
||||
/* End XCRemoteSwiftPackageReference section */
|
||||
|
||||
/* Begin XCSwiftPackageProductDependency section */
|
||||
|
|
@ -1489,11 +1510,6 @@
|
|||
package = CAA858CB25A3D8BC00ACF8C0 /* XCRemoteSwiftPackageReference "ErrorHandling" */;
|
||||
productName = ErrorHandling;
|
||||
};
|
||||
CABFA9DE2592F07A00380FEE /* Path */ = {
|
||||
isa = XCSwiftPackageProductDependency;
|
||||
package = CABFA9DD2592F07A00380FEE /* XCRemoteSwiftPackageReference "Path" */;
|
||||
productName = Path;
|
||||
};
|
||||
CABFA9E32592F08E00380FEE /* Version */ = {
|
||||
isa = XCSwiftPackageProductDependency;
|
||||
package = CABFA9E22592F08E00380FEE /* XCRemoteSwiftPackageReference "Version" */;
|
||||
|
|
@ -1519,11 +1535,25 @@
|
|||
package = CAC28186259EE27200B8AB0B /* XCRemoteSwiftPackageReference "CombineExpectations" */;
|
||||
productName = CombineExpectations;
|
||||
};
|
||||
E8C0EB19291EF43E0081528A /* XcodesKit */ = {
|
||||
isa = XCSwiftPackageProductDependency;
|
||||
productName = XcodesKit;
|
||||
};
|
||||
E8F44A1D296B4CD7002D6592 /* Path */ = {
|
||||
isa = XCSwiftPackageProductDependency;
|
||||
package = E8F44A1C296B4CD7002D6592 /* XCRemoteSwiftPackageReference "Path" */;
|
||||
productName = Path;
|
||||
};
|
||||
E8F81FC3282D8A17006CBD0F /* Sparkle */ = {
|
||||
isa = XCSwiftPackageProductDependency;
|
||||
package = E8F81FC2282D8A17006CBD0F /* XCRemoteSwiftPackageReference "Sparkle" */;
|
||||
productName = Sparkle;
|
||||
};
|
||||
E8FD5726291EE4AC001E004C /* AsyncNetworkService */ = {
|
||||
isa = XCSwiftPackageProductDependency;
|
||||
package = E8FD5725291EE4AC001E004C /* XCRemoteSwiftPackageReference "AsyncHTTPNetworkService" */;
|
||||
productName = AsyncNetworkService;
|
||||
};
|
||||
/* End XCSwiftPackageProductDependency section */
|
||||
};
|
||||
rootObject = CAD2E7962449574E00113D76 /* Project object */;
|
||||
|
|
|
|||
|
|
@ -1,6 +1,15 @@
|
|||
{
|
||||
"object": {
|
||||
"pins": [
|
||||
{
|
||||
"package": "AsyncNetworkService",
|
||||
"repositoryURL": "https://github.com/RobotsAndPencils/AsyncHTTPNetworkService",
|
||||
"state": {
|
||||
"branch": "main",
|
||||
"revision": "97770856c4e429f880d4b4dd68cfaf286dc00c30",
|
||||
"version": null
|
||||
}
|
||||
},
|
||||
{
|
||||
"package": "CombineExpectations",
|
||||
"repositoryURL": "https://github.com/groue/CombineExpectations",
|
||||
|
|
@ -51,8 +60,8 @@
|
|||
"repositoryURL": "https://github.com/mxcl/Path.swift",
|
||||
"state": {
|
||||
"branch": null,
|
||||
"revision": "dac007e907a4f4c565cfdc55a9ce148a761a11d5",
|
||||
"version": "0.16.3"
|
||||
"revision": "9c6f807b0a76be0e27aecc908bc6f173400d839e",
|
||||
"version": "1.4.0"
|
||||
}
|
||||
},
|
||||
{
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ import AppleAPI
|
|||
import Version
|
||||
import LegibleError
|
||||
import os.log
|
||||
import XcodesKit
|
||||
|
||||
/// Downloads and installs Xcodes
|
||||
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 struct XCModel.Xcode
|
||||
import AppleAPI
|
||||
import XcodesKit
|
||||
|
||||
extension AppState {
|
||||
|
||||
|
|
@ -36,6 +37,8 @@ extension AppState {
|
|||
|
||||
func update() {
|
||||
guard !isUpdating else { return }
|
||||
updateDownloadableRuntimes()
|
||||
updateInstalledRuntimes()
|
||||
updatePublisher = updateSelectedXcodePath()
|
||||
.flatMap { _ in
|
||||
self.updateAvailableXcodes(from: self.dataSource)
|
||||
|
|
@ -125,6 +128,21 @@ extension AppState {
|
|||
withIntermediateDirectories: true)
|
||||
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 {
|
||||
|
|
|
|||
|
|
@ -7,9 +7,11 @@ import KeychainAccess
|
|||
import Path
|
||||
import Version
|
||||
import os.log
|
||||
import XcodesKit
|
||||
|
||||
class AppState: ObservableObject {
|
||||
private let client = AppleAPI.Client()
|
||||
internal let runtimeService = RuntimeService()
|
||||
|
||||
// MARK: - Published Properties
|
||||
|
||||
|
|
@ -99,6 +101,12 @@ class AppState: ObservableObject {
|
|||
Current.defaults.set(showOpenInRosettaOption, forKey: "showOpenInRosettaOption")
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Runtimes
|
||||
|
||||
@Published var downloadableRuntimes: [DownloadableRuntime] = []
|
||||
@Published var installedRuntimes: [CoreSimulatorRuntimeInfo] = []
|
||||
|
||||
// MARK: - Publisher Cancellables
|
||||
|
||||
var cancellables = Set<AnyCancellable>()
|
||||
|
|
@ -136,6 +144,7 @@ class AppState: ObservableObject {
|
|||
init() {
|
||||
guard !isTesting else { return }
|
||||
try? loadCachedAvailableXcodes()
|
||||
try? loadCacheDownloadableRuntimes()
|
||||
checkIfHelperIsInstalled()
|
||||
setupAutoInstallTimer()
|
||||
setupDefaults()
|
||||
|
|
@ -783,6 +792,21 @@ class AppState: ObservableObject {
|
|||
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
|
||||
|
||||
private func uninstallXcode(path: Path) -> AnyPublisher<Void, Error> {
|
||||
|
|
|
|||
|
|
@ -1,13 +1,13 @@
|
|||
import Foundation
|
||||
import Path
|
||||
|
||||
extension Entry {
|
||||
static func isAppBundle(kind: Kind, path: Path) -> Bool {
|
||||
kind == .directory &&
|
||||
extension Path {
|
||||
static func isAppBundle(path: Path) -> Bool {
|
||||
path.isDirectory &&
|
||||
path.extension == "app" &&
|
||||
!path.isSymlink
|
||||
}
|
||||
static func infoPlist(kind: Kind, path: Path) -> InfoPlist? {
|
||||
static func infoPlist(path: Path) -> InfoPlist? {
|
||||
let infoPlistPath = path.join("Contents").join("Info.plist")
|
||||
guard
|
||||
let infoPlistData = try? Data(contentsOf: infoPlistPath.url),
|
||||
|
|
@ -18,10 +18,10 @@ extension Entry {
|
|||
}
|
||||
|
||||
var isAppBundle: Bool {
|
||||
Entry.isAppBundle(kind: kind, path: path)
|
||||
Path.isAppBundle(path: self)
|
||||
}
|
||||
|
||||
var infoPlist: InfoPlist? {
|
||||
Entry.infoPlist(kind: kind, path: path)
|
||||
Path.infoPlist(path: self)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ import Foundation
|
|||
import Path
|
||||
import AppleAPI
|
||||
import KeychainAccess
|
||||
|
||||
import XcodesKit
|
||||
/**
|
||||
Lightweight dependency injection using global mutable state :P
|
||||
|
||||
|
|
@ -166,7 +166,7 @@ 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" {
|
||||
if Path.isAppBundle(path: destination) && Path.infoPlist(path: destination)?.bundleID == "com.apple.dt.Xcode" {
|
||||
return InstalledXcode.init(path: destination)
|
||||
} else {
|
||||
return nil
|
||||
|
|
@ -175,9 +175,9 @@ public struct Files {
|
|||
}
|
||||
|
||||
private func _installedXcodes(destination: Path) -> [InstalledXcode] {
|
||||
((try? destination.ls()) ?? [])
|
||||
destination.ls()
|
||||
.filter { $0.isAppBundle && $0.infoPlist?.bundleID == "com.apple.dt.Xcode" }
|
||||
.map { $0.path }
|
||||
.map { $0 }
|
||||
.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
|
||||
}
|
||||
|
||||
static var runtimeCacheFile: Path {
|
||||
return xcodesApplicationSupport/"downloadable-runtimes.json"
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,12 +2,11 @@ import Combine
|
|||
import Foundation
|
||||
import os.log
|
||||
import Path
|
||||
|
||||
public typealias ProcessOutput = (status: Int32, out: String, err: String)
|
||||
import XcodesKit
|
||||
|
||||
extension Process {
|
||||
@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)
|
||||
}
|
||||
|
||||
|
|
@ -67,9 +66,3 @@ extension Process {
|
|||
.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
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
import SwiftUI
|
||||
import XcodesKit
|
||||
|
||||
// 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 {
|
||||
@EnvironmentObject var appState: AppState
|
||||
let xcode: Xcode?
|
||||
|
|
|
|||
|
|
@ -54,6 +54,7 @@ struct InfoPane: View {
|
|||
Divider()
|
||||
|
||||
Group{
|
||||
runtimes(for: xcode)
|
||||
releaseNotes(for: xcode)
|
||||
releaseDate(for: xcode)
|
||||
identicalBuilds(for: xcode)
|
||||
|
|
@ -245,6 +246,29 @@ struct InfoPane: View {
|
|||
.frame(maxWidth: .infinity, maxHeight: .infinity)
|
||||
.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 {
|
||||
|
|
@ -329,6 +353,7 @@ struct InfoPane_Previews: PreviewProvider {
|
|||
),
|
||||
downloadFileSize: 242342424)
|
||||
]
|
||||
|
||||
})
|
||||
.previewDisplayName("Populated, Uninstalled")
|
||||
|
||||
|
|
|
|||
|
|
@ -1,10 +1,37 @@
|
|||
{\rtf1\ansi\ansicpg1252\cocoartf2639
|
||||
{\rtf1\ansi\ansicpg1252\cocoartf2709
|
||||
\cocoatextscaling0\cocoaplatform0{\fonttbl\f0\fnil\fcharset0 .SFNS-Regular;}
|
||||
{\colortbl;\red255\green255\blue255;}
|
||||
{\*\expandedcolortbl;;}
|
||||
\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\
|
||||
|
|
|
|||
|
|
@ -40,6 +40,7 @@
|
|||
"Compilers" = "Compilers";
|
||||
"DownloadSize" = "Download Size";
|
||||
"NoXcodeSelected" = "No Xcode Selected";
|
||||
"Platforms" = "Platforms";
|
||||
|
||||
// Installation Steps
|
||||
// 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