Skip to content

Commit d988c06

Browse files
EvanBaconclaude
andauthored
Add IDEWorkspaceChecks support for workspace check state (#40)
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 d988c06

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)