Skip to content

Commit 78d4201

Browse files
committed
fix: properly type platform mapping and ensure simulator platforms are mapped to device equivalents for clean operations
- Fix TypeScript error by properly typing cleanPlatformMap as Partial<Record<XcodePlatform, XcodePlatform>> - Simulator platforms (iOS Simulator, watchOS Simulator, etc.) are mapped to their device equivalents (iOS, watchOS, etc.) since build products are shared between device and simulator platforms - This allows clean operations to work correctly for all platform types including simulators - All tests passing and local testing confirms iOS, iOS Simulator, and macOS platforms work correctly
1 parent 3db0a27 commit 78d4201

2 files changed

Lines changed: 106 additions & 40 deletions

File tree

src/mcp/tools/utilities/__tests__/clean.test.ts

Lines changed: 55 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -50,54 +50,75 @@ describe('clean (unified) tool', () => {
5050
});
5151

5252
it('uses iOS platform by default', async () => {
53-
const mock = createMockExecutor({ success: true, output: 'clean success' });
54-
const result = await cleanLogic({ projectPath: '/p.xcodeproj', scheme: 'App' } as any, mock);
53+
let capturedCommand: string[] = [];
54+
const mockExecutor = async (command: string[]) => {
55+
capturedCommand = command;
56+
return { success: true, output: 'clean success' };
57+
};
58+
59+
const result = await cleanLogic(
60+
{ projectPath: '/p.xcodeproj', scheme: 'App' } as any,
61+
mockExecutor,
62+
);
5563
expect(result.isError).not.toBe(true);
56-
57-
// Check that the executor was called with iOS platform arguments
58-
expect(mock).toHaveBeenCalled();
59-
const commandArgs = mock.mock.calls[0][0];
60-
expect(commandArgs).toContain('-destination');
61-
expect(commandArgs).toContain('platform=iOS');
64+
65+
// Check that the command contains iOS platform destination
66+
const commandStr = capturedCommand.join(' ');
67+
expect(commandStr).toContain('-destination');
68+
expect(commandStr).toContain('platform=iOS');
6269
});
6370

6471
it('accepts custom platform parameter', async () => {
65-
const mock = createMockExecutor({ success: true, output: 'clean success' });
66-
const result = await cleanLogic({
67-
projectPath: '/p.xcodeproj',
68-
scheme: 'App',
69-
platform: 'macOS'
70-
} as any, mock);
72+
let capturedCommand: string[] = [];
73+
const mockExecutor = async (command: string[]) => {
74+
capturedCommand = command;
75+
return { success: true, output: 'clean success' };
76+
};
77+
78+
const result = await cleanLogic(
79+
{
80+
projectPath: '/p.xcodeproj',
81+
scheme: 'App',
82+
platform: 'macOS',
83+
} as any,
84+
mockExecutor,
85+
);
7186
expect(result.isError).not.toBe(true);
72-
73-
// Check that the executor was called with macOS platform arguments
74-
expect(mock).toHaveBeenCalled();
75-
const commandArgs = mock.mock.calls[0][0];
76-
expect(commandArgs).toContain('-destination');
77-
expect(commandArgs).toContain('platform=macOS');
87+
88+
// Check that the command contains macOS platform destination
89+
const commandStr = capturedCommand.join(' ');
90+
expect(commandStr).toContain('-destination');
91+
expect(commandStr).toContain('platform=macOS');
7892
});
7993

80-
it('accepts iOS Simulator platform parameter', async () => {
81-
const mock = createMockExecutor({ success: true, output: 'clean success' });
82-
const result = await cleanLogic({
83-
projectPath: '/p.xcodeproj',
84-
scheme: 'App',
85-
platform: 'iOS Simulator'
86-
} as any, mock);
94+
it('accepts iOS Simulator platform parameter (maps to iOS for clean)', async () => {
95+
let capturedCommand: string[] = [];
96+
const mockExecutor = async (command: string[]) => {
97+
capturedCommand = command;
98+
return { success: true, output: 'clean success' };
99+
};
100+
101+
const result = await cleanLogic(
102+
{
103+
projectPath: '/p.xcodeproj',
104+
scheme: 'App',
105+
platform: 'iOS Simulator',
106+
} as any,
107+
mockExecutor,
108+
);
87109
expect(result.isError).not.toBe(true);
88-
89-
// Check that the executor was called with iOS Simulator platform arguments
90-
expect(mock).toHaveBeenCalled();
91-
const commandArgs = mock.mock.calls[0][0];
92-
expect(commandArgs).toContain('-destination');
93-
expect(commandArgs).toContain('platform=iOS Simulator');
110+
111+
// For clean operations, iOS Simulator should be mapped to iOS platform
112+
const commandStr = capturedCommand.join(' ');
113+
expect(commandStr).toContain('-destination');
114+
expect(commandStr).toContain('platform=iOS');
94115
});
95116

96117
it('handler validation: rejects invalid platform values', async () => {
97118
const result = await (tool as any).handler({
98119
projectPath: '/p.xcodeproj',
99120
scheme: 'App',
100-
platform: 'InvalidPlatform'
121+
platform: 'InvalidPlatform',
101122
});
102123
expect(result.isError).toBe(true);
103124
const text = String(result.content?.[1]?.text ?? result.content?.[0]?.text ?? '');

src/mcp/tools/utilities/clean.ts

Lines changed: 51 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -33,9 +33,21 @@ const baseOptions = {
3333
'If true, prefers xcodebuild over the experimental incremental build system, useful for when incremental build system fails.',
3434
),
3535
platform: z
36-
.enum(['macOS', 'iOS', 'iOS Simulator', 'watchOS', 'watchOS Simulator', 'tvOS', 'tvOS Simulator', 'visionOS', 'visionOS Simulator'])
36+
.enum([
37+
'macOS',
38+
'iOS',
39+
'iOS Simulator',
40+
'watchOS',
41+
'watchOS Simulator',
42+
'tvOS',
43+
'tvOS Simulator',
44+
'visionOS',
45+
'visionOS Simulator',
46+
])
3747
.optional()
38-
.describe('Optional: Platform to clean for (defaults to iOS). Choose from macOS, iOS, iOS Simulator, watchOS, watchOS Simulator, tvOS, tvOS Simulator, visionOS, visionOS Simulator'),
48+
.describe(
49+
'Optional: Platform to clean for (defaults to iOS). Choose from macOS, iOS, iOS Simulator, watchOS, watchOS Simulator, tvOS, tvOS Simulator, visionOS, visionOS Simulator',
50+
),
3951
};
4052

4153
const baseSchemaObject = z.object({
@@ -71,11 +83,32 @@ export async function cleanLogic(
7183
'Invalid parameters:\nscheme: scheme is required when workspacePath is provided.',
7284
);
7385
}
74-
86+
7587
// Use provided platform or default to iOS
7688
const targetPlatform = params.platform ?? 'iOS';
77-
const platformEnum = XcodePlatform[targetPlatform as keyof typeof XcodePlatform];
78-
89+
90+
// Map human-friendly platform names to XcodePlatform enum values
91+
// This is safer than direct key lookup and handles the space-containing simulator names
92+
const platformMap = {
93+
macOS: XcodePlatform.macOS,
94+
iOS: XcodePlatform.iOS,
95+
'iOS Simulator': XcodePlatform.iOSSimulator,
96+
watchOS: XcodePlatform.watchOS,
97+
'watchOS Simulator': XcodePlatform.watchOSSimulator,
98+
tvOS: XcodePlatform.tvOS,
99+
'tvOS Simulator': XcodePlatform.tvOSSimulator,
100+
visionOS: XcodePlatform.visionOS,
101+
'visionOS Simulator': XcodePlatform.visionOSSimulator,
102+
};
103+
104+
const platformEnum = platformMap[targetPlatform];
105+
if (!platformEnum) {
106+
return createErrorResponse(
107+
'Parameter validation failed',
108+
`Invalid parameters:\nplatform: unsupported value "${targetPlatform}".`,
109+
);
110+
}
111+
79112
const hasProjectPath = typeof params.projectPath === 'string';
80113
const typedParams: SharedBuildParams = {
81114
...(hasProjectPath
@@ -89,10 +122,22 @@ export async function cleanLogic(
89122
extraArgs: params.extraArgs,
90123
};
91124

125+
// For clean operations, simulator platforms should be mapped to their device equivalents
126+
// since clean works at the build product level, not runtime level, and build products
127+
// are shared between device and simulator platforms
128+
const cleanPlatformMap: Partial<Record<XcodePlatform, XcodePlatform>> = {
129+
[XcodePlatform.iOSSimulator]: XcodePlatform.iOS,
130+
[XcodePlatform.watchOSSimulator]: XcodePlatform.watchOS,
131+
[XcodePlatform.tvOSSimulator]: XcodePlatform.tvOS,
132+
[XcodePlatform.visionOSSimulator]: XcodePlatform.visionOS,
133+
};
134+
135+
const cleanPlatform = cleanPlatformMap[platformEnum] ?? platformEnum;
136+
92137
return executeXcodeBuildCommand(
93138
typedParams,
94139
{
95-
platform: platformEnum,
140+
platform: cleanPlatform,
96141
logPrefix: 'Clean',
97142
},
98143
false,

0 commit comments

Comments
 (0)