Skip to content

Commit 4dfb341

Browse files
EvanBaconclaude
andcommitted
Add IDEWorkspaceChecks support for workspace check state
Add support for parsing and building IDEWorkspaceChecks.plist files. Introduced in Xcode 9.3, these store workspace check states to prevent recomputation on each open. Primary use: suppressing the macOS 32-bit deprecation warning via IDEDidComputeMac32BitWarning flag. Features: - Low-level API: parseChecks/buildChecks for plist manipulation - High-level API: IDEWorkspaceChecks class with open/create/save methods - XCWorkspace integration: getWorkspaceChecks, setMac32BitWarningComputed - Convenience property: mac32BitWarningComputed getter/setter Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
1 parent 2772ecb commit 4dfb341

10 files changed

Lines changed: 932 additions & 1 deletion

File tree

README.md

Lines changed: 28 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -277,6 +277,33 @@ Workspace file references use location specifiers:
277277
- `container:path` - Absolute container reference (rare)
278278
- `absolute:path` - Absolute file path
279279

280+
### IDEWorkspaceChecks
281+
282+
Manage `IDEWorkspaceChecks.plist` files that store workspace check states. Introduced in Xcode 9.3, these files prevent Xcode from recomputing checks each time a workspace is opened.
283+
284+
The primary use is suppressing the macOS 32-bit deprecation warning:
285+
286+
```ts
287+
import { XCWorkspace, IDEWorkspaceChecks } from "@bacons/xcode";
288+
289+
// Suppress the 32-bit deprecation warning
290+
const workspace = XCWorkspace.open("/path/to/MyApp.xcworkspace");
291+
workspace.setMac32BitWarningComputed();
292+
293+
// Or work with IDEWorkspaceChecks directly
294+
const checks = IDEWorkspaceChecks.openOrCreate("/path/to/MyApp.xcworkspace");
295+
checks.mac32BitWarningComputed = true;
296+
checks.save();
297+
298+
// Low-level API
299+
import * as workspace from "@bacons/xcode/workspace";
300+
301+
const plist = workspace.parseChecks(plistString);
302+
console.log(plist.IDEDidComputeMac32BitWarning); // true
303+
304+
const output = workspace.buildChecks({ IDEDidComputeMac32BitWarning: true });
305+
```
306+
280307
## XCConfig Support
281308

282309
Parse and manipulate Xcode configuration files (`.xcconfig`). These files define build settings that can be shared across targets and configurations.
@@ -496,7 +523,7 @@ We support the following types: `Object`, `Array`, `Data`, `String`. Notably, we
496523
- [ ] Skills.
497524
- [ ] Import from other tools.
498525
- [ ] **XCUserData**: (`xcuserdata/<user>.xcuserdatad/`) Per-user schemes, breakpoints, UI state.
499-
- [ ] **IDEWorkspaceChecks**: (`xcshareddata/IDEWorkspaceChecks.plist`) "Trust this project" flag that suppresses Xcode warning.
526+
- [x] **IDEWorkspaceChecks**: (`xcshareddata/IDEWorkspaceChecks.plist`) Workspace check state storage (e.g., 32-bit deprecation warning).
500527

501528
# Docs
502529

src/api/IDEWorkspaceChecks.ts

Lines changed: 176 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,176 @@
1+
/**
2+
* High-level API for IDEWorkspaceChecks.plist files.
3+
*
4+
* Introduced in Xcode 9.3, these files store the state of workspace checks
5+
* to prevent them from being recomputed each time the workspace is opened.
6+
*
7+
* Currently known keys:
8+
* - IDEDidComputeMac32BitWarning: Tracks whether the 32-bit macOS deprecation
9+
* warning has been computed/shown. Setting to true suppresses the warning.
10+
*/
11+
import { readFileSync, writeFileSync, existsSync, mkdirSync } from "fs";
12+
import path from "path";
13+
14+
import { parseChecks, buildChecks } from "../workspace/checks";
15+
import type { IDEWorkspaceChecks as ChecksData } from "../workspace/types";
16+
17+
/** Relative path from workspace to the checks plist */
18+
const CHECKS_PATH = "xcshareddata/IDEWorkspaceChecks.plist";
19+
20+
/**
21+
* High-level class for working with IDEWorkspaceChecks.plist files.
22+
*/
23+
export class IDEWorkspaceChecks {
24+
/** The parsed checks data */
25+
props: ChecksData;
26+
27+
/** Path to the plist file (may be undefined for new instances) */
28+
filePath?: string;
29+
30+
private constructor(props: ChecksData, filePath?: string) {
31+
this.props = props;
32+
this.filePath = filePath;
33+
}
34+
35+
/**
36+
* Open an existing IDEWorkspaceChecks.plist from a workspace.
37+
*
38+
* @param workspacePath Path to the .xcworkspace directory
39+
* @returns The checks instance, or null if the file doesn't exist
40+
*/
41+
static open(workspacePath: string): IDEWorkspaceChecks | null {
42+
const checksPath = path.join(workspacePath, CHECKS_PATH);
43+
if (!existsSync(checksPath)) {
44+
return null;
45+
}
46+
47+
const plistString = readFileSync(checksPath, "utf-8");
48+
const props = parseChecks(plistString);
49+
return new IDEWorkspaceChecks(props, checksPath);
50+
}
51+
52+
/**
53+
* Open an existing IDEWorkspaceChecks.plist or create a new one.
54+
*
55+
* @param workspacePath Path to the .xcworkspace directory
56+
* @returns The checks instance (opened or newly created)
57+
*/
58+
static openOrCreate(workspacePath: string): IDEWorkspaceChecks {
59+
const existing = IDEWorkspaceChecks.open(workspacePath);
60+
if (existing) {
61+
return existing;
62+
}
63+
64+
const checksPath = path.join(workspacePath, CHECKS_PATH);
65+
return new IDEWorkspaceChecks(
66+
{ IDEDidComputeMac32BitWarning: true },
67+
checksPath
68+
);
69+
}
70+
71+
/**
72+
* Create a new IDEWorkspaceChecks instance.
73+
*
74+
* @param options Optional initial props and file path
75+
*/
76+
static create(options?: {
77+
props?: Partial<ChecksData>;
78+
filePath?: string;
79+
}): IDEWorkspaceChecks {
80+
const defaultProps: ChecksData = {
81+
IDEDidComputeMac32BitWarning: true,
82+
};
83+
84+
const props = { ...defaultProps, ...options?.props };
85+
return new IDEWorkspaceChecks(props, options?.filePath);
86+
}
87+
88+
/**
89+
* Save the checks to disk.
90+
*
91+
* @param filePath Optional path to save to. If not provided, uses this.filePath.
92+
*/
93+
save(filePath?: string): void {
94+
const targetPath = filePath ?? this.filePath;
95+
if (!targetPath) {
96+
throw new Error(
97+
"No file path specified. Either provide a path or set this.filePath."
98+
);
99+
}
100+
101+
// Ensure parent directory exists
102+
const dir = path.dirname(targetPath);
103+
if (!existsSync(dir)) {
104+
mkdirSync(dir, { recursive: true });
105+
}
106+
107+
const plistString = buildChecks(this.props);
108+
writeFileSync(targetPath, plistString, "utf-8");
109+
this.filePath = targetPath;
110+
}
111+
112+
/**
113+
* Save the checks to a workspace directory.
114+
*
115+
* @param workspacePath Path to the .xcworkspace directory
116+
*/
117+
saveToWorkspace(workspacePath: string): void {
118+
const checksPath = path.join(workspacePath, CHECKS_PATH);
119+
this.save(checksPath);
120+
}
121+
122+
/**
123+
* Get the plist representation of the checks.
124+
*/
125+
toPlist(): string {
126+
return buildChecks(this.props);
127+
}
128+
129+
/**
130+
* Get whether the Mac 32-bit warning has been computed/dismissed.
131+
*/
132+
get mac32BitWarningComputed(): boolean {
133+
return this.props.IDEDidComputeMac32BitWarning ?? false;
134+
}
135+
136+
/**
137+
* Set whether the Mac 32-bit warning has been computed/dismissed.
138+
*/
139+
set mac32BitWarningComputed(value: boolean) {
140+
this.props.IDEDidComputeMac32BitWarning = value;
141+
}
142+
143+
/**
144+
* Get a check value by key.
145+
*
146+
* @param key The check key
147+
* @returns The boolean value, or undefined if not set
148+
*/
149+
getCheck(key: string): boolean | undefined {
150+
return this.props[key];
151+
}
152+
153+
/**
154+
* Set a check value.
155+
*
156+
* @param key The check key
157+
* @param value The boolean value
158+
*/
159+
setCheck(key: string, value: boolean): void {
160+
this.props[key] = value;
161+
}
162+
163+
/**
164+
* Remove a check.
165+
*
166+
* @param key The check key
167+
* @returns true if the check was removed, false if it didn't exist
168+
*/
169+
removeCheck(key: string): boolean {
170+
if (key in this.props) {
171+
delete this.props[key];
172+
return true;
173+
}
174+
return false;
175+
}
176+
}

src/api/XCWorkspace.ts

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import path from "path";
88

99
import * as workspace from "../workspace";
1010
import type { XCWorkspace as WorkspaceData, FileRef, Group } from "../workspace/types";
11+
import { IDEWorkspaceChecks } from "./IDEWorkspaceChecks";
1112
import { XCSharedData } from "./XCSharedData";
1213

1314
/**
@@ -236,6 +237,56 @@ export class XCWorkspace {
236237
});
237238
}
238239

240+
/**
241+
* Get the IDEWorkspaceChecks for this workspace.
242+
*
243+
* @returns The checks instance, or null if not set
244+
*/
245+
getWorkspaceChecks(): IDEWorkspaceChecks | null {
246+
if (!this.filePath) {
247+
return null;
248+
}
249+
return IDEWorkspaceChecks.open(this.filePath);
250+
}
251+
252+
/**
253+
* Get or create the IDEWorkspaceChecks for this workspace.
254+
*
255+
* @returns The checks instance (opened or newly created)
256+
*/
257+
getOrCreateWorkspaceChecks(): IDEWorkspaceChecks {
258+
if (!this.filePath) {
259+
throw new Error(
260+
"Workspace must be saved before accessing workspace checks."
261+
);
262+
}
263+
return IDEWorkspaceChecks.openOrCreate(this.filePath);
264+
}
265+
266+
/**
267+
* Check if this workspace has IDEWorkspaceChecks configured.
268+
*
269+
* @returns true if the checks plist exists
270+
*/
271+
hasWorkspaceChecks(): boolean {
272+
if (!this.filePath) {
273+
return false;
274+
}
275+
return IDEWorkspaceChecks.open(this.filePath) !== null;
276+
}
277+
278+
/**
279+
* Mark the 32-bit warning as computed.
280+
*
281+
* This suppresses the macOS 32-bit deprecation warning dialog in Xcode
282+
* by setting IDEDidComputeMac32BitWarning to true.
283+
*/
284+
setMac32BitWarningComputed(): void {
285+
const checks = this.getOrCreateWorkspaceChecks();
286+
checks.mac32BitWarningComputed = true;
287+
checks.save();
288+
}
289+
239290
private collectPathsFromGroup(group: Group): string[] {
240291
const paths: string[] = [];
241292

0 commit comments

Comments
 (0)