Skip to content

Commit 3f15763

Browse files
authored
Merge pull request #25 from nab138/xcode26
Xcode 26 Support
2 parents d22b8e8 + 8d0b000 commit 3f15763

File tree

13 files changed

+251
-84
lines changed

13 files changed

+251
-84
lines changed

README.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414

1515
iOS Swift development IDE for Windows/Linux. Create, build, and test apps without owning a Mac.
1616

17-
Supports Swift 6.1 and the Swift Package Manager.
17+
Supports Swift 6.2 and the Swift Package Manager.
1818

1919
### Demo
2020

@@ -30,7 +30,7 @@ Check out the [Getting Started](https://github.com/nab138/CrossCode/wiki#getting
3030

3131
## Features
3232

33-
- Generate a Darwin SDK for linux from a user provided copy of Xcode 16.3 to build the apps
33+
- Generate a Darwin SDK for linux from a user provided copy of Xcode 26 to build the apps
3434
- Build apps using swift package manager
3535
- Log in with your Apple ID to sign apps
3636
- Install apps on device
@@ -49,7 +49,7 @@ Please note that I am one person, so development may be slow. If you want to hel
4949

5050
## How it works
5151

52-
- A darwin SDK is generated from a user provided copy of Xcode 16.3 (extracted with [unxip-rs](https://github.com/nab138/unxip-rs)) and darwin tools from [darwin-tools-linux-llvm](https://github.com/xtool-org/darwin-tools-linux-llvm)
52+
- A darwin SDK is generated from a user provided copy of Xcode 26 (extracted with [unxip-rs](https://github.com/nab138/unxip-rs)) and darwin tools from [darwin-tools-linux-llvm](https://github.com/xtool-org/darwin-tools-linux-llvm)
5353
- Swift uses the darwin SDK to build an executable which is packaged into an .app bundle.
5454
- The code to sign and install apps onto a device has been removed from CrossCode's source and moved to a standalone package, [isideload](https://github.com/nab138/isideload). It was built on a lot of other libraries, so check out its README for more info.
5555

bun.lock

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@
5050
"@types/vscode": "^1.102.0",
5151
"@vitejs/plugin-react": "^4.2.1",
5252
"typescript": "^5.0.2",
53-
"vite": "^7.0.0",
53+
"vite": "^7.1.6",
5454
},
5555
},
5656
},

package.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,6 @@
5959
"@types/vscode": "^1.102.0",
6060
"@vitejs/plugin-react": "^4.2.1",
6161
"typescript": "^5.0.2",
62-
"vite": "^7.0.0"
62+
"vite": "^7.1.6"
6363
}
64-
}
64+
}

src-tauri/src/builder/swift.rs

Lines changed: 42 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -159,24 +159,61 @@ impl SwiftBin {
159159
}
160160

161161
#[tauri::command]
162-
pub fn has_darwin_sdk(toolchain_path: &str) -> bool {
162+
pub fn has_darwin_sdk(toolchain_path: &str) -> String {
163163
let swift_bin = SwiftBin::new(toolchain_path);
164164
if swift_bin.is_err() {
165-
return false;
165+
return "none".to_string();
166166
}
167167
let swift_bin = swift_bin.unwrap();
168168

169169
let output = swift_bin.output(&["sdk", "list"]);
170170
if output.is_err() {
171-
return false;
171+
return "none".to_string();
172172
}
173173
let output = output.unwrap();
174174
if !output.status.success() {
175-
return false;
175+
return "none".to_string();
176+
}
177+
let output_str = String::from_utf8_lossy(&output.stdout);
178+
if !output_str.contains("darwin") {
179+
return "none".to_string();
180+
}
181+
182+
let output = swift_bin.output(&[
183+
"sdk",
184+
"configure",
185+
"--show-configuration",
186+
"darwin",
187+
"arm64-apple-ios",
188+
]);
189+
if output.is_err() {
190+
return "none".to_string();
191+
}
192+
let output = output.unwrap();
193+
if !output.status.success() {
194+
return "none".to_string();
176195
}
196+
177197
let output_str = String::from_utf8_lossy(&output.stdout);
178198

179-
output_str.contains("darwin")
199+
let sdk_version = output_str.lines().find_map(|line| {
200+
if line.starts_with("sdkRootPath:") {
201+
let parts: Vec<&str> = line.split('/').collect();
202+
for part in parts {
203+
if part.starts_with("iPhoneOS") && part.ends_with(".sdk") {
204+
let version = part.trim_start_matches("iPhoneOS").trim_end_matches(".sdk");
205+
return Some(version.to_string());
206+
}
207+
}
208+
}
209+
None
210+
});
211+
212+
if let Some(version) = sdk_version {
213+
version
214+
} else {
215+
"none".to_string()
216+
}
180217
}
181218

182219
#[tauri::command]

src-tauri/src/lsp_utils.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ pub fn validate_project(project_path: String, toolchain_path: String) -> Project
3232
// let config_path = project_path.join(".sourcekit-lsp").join("config.json");
3333
// if !config_path.exists() {
3434
// fs::write(config_path, "{
35-
// \"$schema\": \"https://raw.githubusercontent.com/swiftlang/sourcekit-lsp/refs/heads/release/6.1/config.schema.json\",
35+
// \"$schema\": \"https://raw.githubusercontent.com/swiftlang/sourcekit-lsp/refs/heads/release/6.2/config.schema.json\",
3636
// \"swiftPM\": {
3737
// \"swiftSDK\": \"arm64-apple-ios\"
3838
// }

src-tauri/templates/swiftui/Package.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// swift-tools-version: 6.1
1+
// swift-tools-version: 6.2
22

33
import PackageDescription
44

src-tauri/templates/uikit/Package.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// swift-tools-version: 6.1
1+
// swift-tools-version: 6.2
22

33
import PackageDescription
44

src/components/SDKMenu.tsx

Lines changed: 42 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -7,11 +7,13 @@ import { openUrl } from "@tauri-apps/plugin-opener";
77
import { installSdkOperation } from "../utilities/operations";
88
import ErrorIcon from "@mui/icons-material/Error";
99
import WarningIcon from "@mui/icons-material/Warning";
10+
import { DARWIN_SDK_VERSION } from "../utilities/constants";
1011

1112
export default () => {
1213
const {
1314
selectedToolchain,
1415
hasDarwinSDK,
16+
darwinSDKVersion,
1517
checkSDK,
1618
startOperation,
1719
isWindows,
@@ -86,32 +88,46 @@ export default () => {
8688
gap: "var(--padding-md)",
8789
}}
8890
>
89-
<Typography
90-
level="body-md"
91-
color={
92-
hasDarwinSDK ? "success" : selectedToolchain ? "danger" : "warning"
93-
}
94-
sx={{
95-
alignContent: "center",
96-
display: "flex",
97-
gap: "var(--padding-xs)",
98-
}}
99-
>
100-
{isWindowsReady ? (
101-
hasDarwinSDK ? (
102-
"Darwin SDK is installed!"
91+
<div>
92+
<Typography
93+
level="body-md"
94+
color={
95+
hasDarwinSDK ? "success" : selectedToolchain ? "danger" : "warning"
96+
}
97+
sx={{
98+
alignContent: "center",
99+
display: "flex",
100+
gap: "var(--padding-xs)",
101+
}}
102+
>
103+
{isWindowsReady ? (
104+
hasDarwinSDK ? (
105+
"Darwin SDK is installed!"
106+
) : (
107+
<>
108+
{selectedToolchain ? <ErrorIcon /> : <WarningIcon />}
109+
{selectedToolchain
110+
? "Darwin SDK is not installed"
111+
: "Select a swift toolchain first"}
112+
</>
113+
)
103114
) : (
104-
<>
105-
{selectedToolchain ? <ErrorIcon /> : <WarningIcon />}
106-
{selectedToolchain
107-
? "Darwin SDK is not installed"
108-
: "Select a swift toolchain first"}
109-
</>
110-
)
111-
) : (
112-
"Install WSL and Swift first."
115+
"Install WSL and Swift first."
116+
)}
117+
</Typography>
118+
{hasDarwinSDK && (
119+
<Typography
120+
level="body-sm"
121+
color={
122+
darwinSDKVersion === DARWIN_SDK_VERSION ? undefined : "warning"
123+
}
124+
>
125+
{darwinSDKVersion === DARWIN_SDK_VERSION
126+
? `Version: ${darwinSDKVersion}`
127+
: `Unsupported SDK version (${darwinSDKVersion}). Apps may compile, but you may not be able to use newer features (like liquid glass). Please re-install with Xcode 26.`}
128+
</Typography>
113129
)}
114-
</Typography>
130+
</div>
115131
<div
116132
style={{
117133
display: "flex",
@@ -123,11 +139,11 @@ export default () => {
123139
onClick={(e) => {
124140
e.preventDefault();
125141
openUrl(
126-
"https://developer.apple.com/services-account/download?path=/Developer_Tools/Xcode_16.3/Xcode_16.3.xip"
142+
"https://developer.apple.com/services-account/download?path=/Developer_Tools/Xcode_26/Xcode_26_Universal.xip"
127143
);
128144
}}
129145
>
130-
Download XCode 16.3
146+
Download XCode 26
131147
</Button>
132148
<Button
133149
variant="soft"

src/components/SwiftMenu.tsx

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import { useEffect, useState } from "react";
1111
import { openUrl } from "@tauri-apps/plugin-opener";
1212
import ErrorIcon from "@mui/icons-material/Error";
1313
import { invoke } from "@tauri-apps/api/core";
14+
import { SWIFT_VERSION_PREFIX } from "../utilities/constants";
1415

1516
export default () => {
1617
const {
@@ -92,7 +93,7 @@ export default () => {
9293
fontFamily: "monospace",
9394
}}
9495
>
95-
swiftly install 6.1
96+
swiftly install {SWIFT_VERSION_PREFIX}
9697
</span>
9798
" or manually. If you have already done so, but it is not showing up,
9899
your toolchain installation may be broken. For help, refer to the{" "}
@@ -110,6 +111,13 @@ export default () => {
110111
.
111112
</Typography>
112113
)}
114+
{selectedToolchain !== null && !isCompatable(selectedToolchain) && (
115+
<Typography level="body-md" color="danger">
116+
Your selected toolchain is not compatible. Please select a swift{" "}
117+
{SWIFT_VERSION_PREFIX}
118+
toolchain.
119+
</Typography>
120+
)}
113121
{toolchains !== null && allToolchains.length > 0 && (
114122
<div>
115123
<Typography level="body-md">Select a toolchain:</Typography>
@@ -195,9 +203,9 @@ export default () => {
195203
);
196204
};
197205

198-
function isCompatable(toolchain: Toolchain | null): boolean {
206+
export function isCompatable(toolchain: Toolchain | null): boolean {
199207
if (!toolchain) return false;
200-
return toolchain.version.startsWith("6.1");
208+
return toolchain.version.startsWith(SWIFT_VERSION_PREFIX);
201209
}
202210

203211
function stringifyToolchain(toolchain: Toolchain | null): string | null {

src/pages/IDE.tsx

Lines changed: 73 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ import { restartServer } from "../utilities/lsp-client";
2525
import BottomBar from "../components/Tiles/BottomBar";
2626
import { open as openFileDialog } from "@tauri-apps/plugin-dialog";
2727
import { IStandaloneCodeEditor } from "@codingame/monaco-vscode-api/vscode/vs/editor/standalone/browser/standaloneCodeEditor";
28+
import { DARWIN_SDK_VERSION } from "../utilities/constants";
2829

2930
export interface IDEProps {}
3031

@@ -46,8 +47,14 @@ export default () => {
4647
const [redo, setRedo] = useState<(() => void) | null>(null);
4748
const [theme] = useStore<"light" | "dark">("appearance/theme", "dark");
4849
const { path } = useParams<"path">();
49-
const { openFolderDialog, selectedToolchain, hasLimitedRam, initialized } =
50-
useIDE();
50+
const {
51+
openFolderDialog,
52+
selectedToolchain,
53+
hasLimitedRam,
54+
initialized,
55+
ready,
56+
darwinSDKVersion,
57+
} = useIDE();
5158
const [sourcekitStartup, setSourcekitStartup] = useStore<boolean | null>(
5259
"sourcekit/startup",
5360
null
@@ -57,6 +64,11 @@ export default () => {
5764
false
5865
);
5966

67+
const [hasIgnoredDarwinSDK, setHasIgnoredDarwinSDK] = useStore<boolean>(
68+
"has-ignored-darwin-sdk",
69+
false
70+
);
71+
6072
if (!path) {
6173
throw new Error("Path parameter is required in IDE component");
6274
}
@@ -70,6 +82,17 @@ export default () => {
7082
const [editor, setEditor] = useState<IStandaloneCodeEditor | null>(null);
7183
const { addToast } = useToast();
7284

85+
useEffect(() => {
86+
if (ready === false && initialized) {
87+
console.log(
88+
"IDE not ready, returning to welcome page",
89+
ready,
90+
initialized
91+
);
92+
navigate("/");
93+
}
94+
}, [ready, initialized, navigate]);
95+
7396
useEffect(() => {
7497
(async () => {
7598
if (!store || !storeInitialized || !path) return;
@@ -327,6 +350,54 @@ export default () => {
327350
</ModalDialog>
328351
</Modal>
329352
)}
353+
{initialized &&
354+
selectedToolchain !== null &&
355+
hasIgnoredDarwinSDK === false &&
356+
darwinSDKVersion !== DARWIN_SDK_VERSION && (
357+
<Modal
358+
open={true}
359+
onClose={() => {
360+
setHasIgnoredDarwinSDK(true);
361+
}}
362+
>
363+
<ModalDialog sx={{ maxWidth: "90vw" }}>
364+
<ModalClose />
365+
<div>
366+
<div style={{ display: "flex", gap: "var(--padding-md)" }}>
367+
<div style={{ width: "1.25rem" }}>
368+
<WarningIcon />
369+
</div>
370+
<Typography level="h3">Incompatible SDK Version</Typography>
371+
</div>
372+
<Typography level="body-lg">
373+
This version of CrossCode is designed to work with Darwin SDK{" "}
374+
{DARWIN_SDK_VERSION}, but you have version {darwinSDKVersion}{" "}
375+
installed. Things may still work, but you will miss out on
376+
newer features (like liquid glass) and may run into issues.
377+
</Typography>
378+
</div>
379+
380+
<Divider sx={{ mb: "var(--padding-xs)" }} />
381+
<div style={{ display: "flex", gap: "var(--padding-lg)" }}>
382+
<Button
383+
onClick={() => {
384+
navigate("/#install-sdk");
385+
}}
386+
>
387+
Install Correct SDK
388+
</Button>
389+
<Button
390+
onClick={() => {
391+
setHasIgnoredDarwinSDK(true);
392+
}}
393+
variant="outlined"
394+
>
395+
Ignore
396+
</Button>
397+
</div>
398+
</ModalDialog>
399+
</Modal>
400+
)}
330401
</div>
331402
);
332403
};

0 commit comments

Comments
 (0)