Skip to content

Commit 461272e

Browse files
authored
Merge pull request #260 from tidev/feature/xcode-27-device-hub
feat: support Xcode 27
2 parents 67528c4 + e472857 commit 461272e

6 files changed

Lines changed: 110 additions & 206 deletions

File tree

lib/simulator.js

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,14 @@ function compareSims(a, b) {
8383
return a.model < b.model ? -1 : a.model > b.model ? 1 : 0;
8484
}
8585

86+
function getCreateSimulatorHelpText(selectedXcode) {
87+
if (selectedXcode && selectedXcode.version && appc.version.gte(selectedXcode.version, '27.0')) {
88+
return '\n\nPlease open "Device Hub" app and create a new Simulator with your preferred configuration.';
89+
}
90+
91+
return '\n\nPlease open Xcode, navigate to "Window > Devices and Simulators" and create a new Simulator with your preferred configuration.';
92+
}
93+
8694
/**
8795
* Detects iOS simulators.
8896
*
@@ -770,7 +778,7 @@ function findSimulators(options, callback) {
770778
}
771779

772780
if (!simHandle) {
773-
const helpText = '\n\nPlease open Xcode, navigate to "Window > Devices and Simulators" and create a new Simulator with your preferred configuration.';
781+
const helpText = getCreateSimulatorHelpText(selectedXcode);
774782

775783
// user experience!
776784
if (options.simVersion) {

lib/xcode.js

Lines changed: 17 additions & 198 deletions
Original file line numberDiff line numberDiff line change
@@ -40,204 +40,6 @@ var cache,
4040
* what Watch Simulator. It's a mystery!
4141
*/
4242
const simulatorDevicePairCompatibility = {
43-
'>=6.2 <7.0': { // Xcode 6.2, 6.3, 6.4
44-
'>=8.2 <9.0': { // iOS 8.2, 8.3, 8.4
45-
'1.x': true // watchOS 1.0
46-
}
47-
},
48-
'7.x': { // Xcode 7.x
49-
'>=8.2 <9.0': { // iOS 8.2, 8.3, 8.4
50-
'1.x': true // watchOS 1.0
51-
},
52-
'>=9.0 <=9.2': { // iOS 9.0, 9.1, 9.2
53-
'>=2.0 <=2.1': true // watchOS 2.0, 2.1
54-
},
55-
'>=9.3 <10': { // iOS 9.3
56-
'2.2': true // watchOS 2.2
57-
}
58-
},
59-
'8.x': { // Xcode 8.x
60-
'>=9.0 <=9.2': { // iOS 9.0, 9.1, 9.2
61-
'>=2.0 <=2.1': true // watchOS 2.0, 2.1
62-
},
63-
'>=9.3 <10': { // iOS 9.x
64-
'2.2': true, // watchOS 2.2
65-
'3.x': true // watchOS 3.x
66-
},
67-
'10.x': { // iOS 10.x
68-
'2.2': true, // watchOS 2.2
69-
'3.x': true // watchOS 3.x
70-
}
71-
},
72-
'9.x': { // Xcode 9.x
73-
'>=9.0 <=9.2': { // iOS 9.0, 9.1, 9.2
74-
'>=2.0 <=2.1': true // watchOS 2.0, 2.1
75-
},
76-
'>=9.3 <10': { // iOS 9.x
77-
'2.2': true, // watchOS 2.2
78-
'3.x': true // watchOS 3.x
79-
},
80-
'10.x': { // iOS 10.x
81-
'2.2': true, // watchOS 2.2
82-
'3.x': true // watchOS 3.x
83-
},
84-
'11.x': { // iOS 11.x
85-
'>=3.2 <4.0': true, // watchOS 3.2
86-
'4.x': true // watchOS 4.x
87-
}
88-
},
89-
'10.x <10.3': { // Xcode 10.0-10.2.1
90-
'8.x': {}, // iOS 8.x
91-
'>=9.0 <=9.2': { // iOS 9.0, 9.1, 9.2
92-
'>=2.0 <=2.1': true // watchOS 2.0, 2.1
93-
},
94-
'>=9.3 <10': { // iOS 9.x
95-
'2.2': true, // watchOS 2.2
96-
'3.x': true // watchOS 3.x
97-
},
98-
'>=10.0 <=10.2': { // iOS 10.0, 10.1, 10.2
99-
'2.2': true, // watchOS 2.2
100-
'3.x': true // watchOS 3.x
101-
},
102-
'>=10.3 <11': { // iOS 10.3
103-
'3.x': true // watchOS 3.x
104-
},
105-
'11.x': { // iOS 11.x
106-
'>=3.2 <4.0': true, // watchOS 3.2
107-
'4.x': true // watchOS 4.x
108-
},
109-
'12.x': { // iOS 12.x
110-
'>=3.2 <4.0': true, // watchOS 3.2
111-
'4.x': true, // watchOS 4.x
112-
'5.x': true // watchOS 5.x
113-
}
114-
},
115-
'>=10.3 <11': {
116-
'>=10.3 <11': { // iOS 10.3
117-
'3.x': true // watchOS 3.x
118-
},
119-
'11.x': { // iOS 11.x
120-
'>=3.2 <4.0': true, // watchOS 3.2
121-
'4.x': true // watchOS 4.x
122-
},
123-
'12.x': { // iOS 12.x
124-
'4.x': true, // watchOS 4.x
125-
'5.x': true // watchOS 5.x
126-
}
127-
},
128-
'11.x': { // Xcode 11.x
129-
'>=10.3 <11': { // iOS 10.3
130-
'2.2': true, // watchOS 2.2
131-
'3.x': true // watchOS 3.x
132-
},
133-
'11.x': { // iOS 11.x
134-
'>=3.2 <4.0': true, // watchOS 3.2
135-
'4.x': true // watchOS 4.x
136-
},
137-
'12.x': { // iOS 12.x
138-
'4.x': true, // watchOS 4.x
139-
'5.x': true, // watchOS 5.x
140-
'6.x': true // watchOS 6.x
141-
},
142-
'13.x': { // iOS 13.x
143-
'4.x': true, // watchOS 4.x
144-
'5.x': true, // watchOS 5.x
145-
'6.x': true // watchOS 6.x
146-
}
147-
},
148-
'12.x': { // Xcode 12.x
149-
'>=10.3 <11': { // iOS 10.x
150-
'2.2': true, // watchOS 2.2
151-
'3.x': true // watchOS 3.x
152-
},
153-
'11.x': { // iOS 11.x
154-
'>=3.2 <4.0': true, // watchOS 3.2
155-
'4.x': true // watchOS 4.x
156-
},
157-
'12.x': { // iOS 12.x
158-
'4.x': true, // watchOS 4.x
159-
'5.x': true, // watchOS 5.x
160-
'6.x': true, // watchOS 6.x
161-
'7.x': true // watchOS 7.x
162-
},
163-
'13.x': { // iOS 13.x
164-
'4.x': true, // watchOS 4.x
165-
'5.x': true, // watchOS 5.x
166-
'6.x': true, // watchOS 6.x
167-
'7.x': true // watchOS 7.x
168-
},
169-
'14.x': { // iOS 14.x
170-
'4.x': true, // watchOS 4.x
171-
'5.x': true, // watchOS 5.x
172-
'6.x': true, // watchOS 6.x
173-
'7.x': true // watchOS 7.x
174-
}
175-
},
176-
'13.x': { // Xcode 13.x
177-
'>=10.3 <11': { // iOS 10.x
178-
'2.2': true, // watchOS 2.2
179-
'3.x': true // watchOS 3.x
180-
},
181-
'11.x': { // iOS 11.x
182-
'>=3.2 <4.0': true, // watchOS 3.2
183-
'4.x': true // watchOS 4.x
184-
},
185-
'12.x': { // iOS 12.x
186-
'4.x': true, // watchOS 4.x
187-
'5.x': true, // watchOS 5.x
188-
'6.x': true, // watchOS 6.x
189-
'7.x': true, // watchOS 7.x
190-
'8.x': true // watchOS 8.x
191-
},
192-
'13.x': { // iOS 13.x
193-
'4.x': true, // watchOS 4.x
194-
'5.x': true, // watchOS 5.x
195-
'6.x': true, // watchOS 6.x
196-
'7.x': true, // watchOS 7.x
197-
'8.x': true // watchOS 8.x
198-
},
199-
'14.x': { // iOS 14.x
200-
'4.x': true, // watchOS 4.x
201-
'5.x': true, // watchOS 5.x
202-
'6.x': true, // watchOS 6.x
203-
'7.x': true, // watchOS 7.x
204-
'8.x': true // watchOS 8.x
205-
},
206-
'15.x': {
207-
'4.x': true, // watchOS 4.x
208-
'5.x': true, // watchOS 5.x
209-
'6.x': true, // watchOS 6.x
210-
'7.x': true, // watchOS 7.x
211-
'8.x': true // watchOS 8.x
212-
}
213-
},
214-
'14.x': { // Xcode 14.x
215-
'12.x': { // iOS 12.x
216-
'7.x': true, // watchOS 7.x
217-
'8.x': true, // watchOS 8.x
218-
'9.x': true // watchOS 9.x
219-
},
220-
'13.x': { // iOS 13.x
221-
'7.x': true, // watchOS 7.x
222-
'8.x': true, // watchOS 8.x
223-
'9.x': true // watchOS 9.x
224-
},
225-
'14.x': { // iOS 14.x
226-
'7.x': true, // watchOS 7.x
227-
'8.x': true, // watchOS 8.x
228-
'9.x': true // watchOS 9.x
229-
},
230-
'15.x': {
231-
'7.x': true, // watchOS 7.x
232-
'8.x': true, // watchOS 8.x
233-
'9.x': true // watchOS 9.x
234-
},
235-
'16.x': {
236-
'7.x': true, // watchOS 7.x
237-
'8.x': true, // watchOS 8.x
238-
'9.x': true // watchOS 9.x
239-
}
240-
},
24143
'15.x': { // Xcode 15.x
24244
'13.x': { // iOS 13.x
24345
'7.x': true, // watchOS 7.x
@@ -315,6 +117,16 @@ const simulatorDevicePairCompatibility = {
315117
'11.x': true, // watchOS 11.x
316118
'26.x': true, // watchOS 26.x
317119
},
120+
},
121+
'27.x': { // Xcode 27.x
122+
'26.x': { // iOS 26.x
123+
'26.x': true, // watchOS 26.x
124+
'27.x': true, // watchOS 27.x
125+
},
126+
'27.x': { // iOS 27.x
127+
'26.x': true, // watchOS 26.x
128+
'27.x': true, // watchOS 27.x
129+
},
318130
}
319131
};
320132

@@ -641,6 +453,13 @@ exports.detect = function detect(options, callback) {
641453
}
642454
});
643455

456+
if (!xc.executables.simulator) {
457+
var deviceHub = path.join(dir, '..', 'Applications', 'DeviceHub.app', 'Contents', 'MacOS', 'DeviceHub');
458+
if (fs.existsSync(deviceHub)) {
459+
xc.executables.simulator = deviceHub;
460+
}
461+
}
462+
644463
if (appc.version.gte(xc.version, 9)) {
645464
xc.executables.watchsimulator = xc.executables.simulator;
646465
} else {

test/test-ioslib.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,8 @@ describe('ioslib', function () {
1919
});
2020

2121
it('detect all iOS information', function (done) {
22-
this.timeout(80000);
23-
this.slow(30000);
22+
this.timeout(180000);
23+
this.slow(60000);
2424

2525
ioslib.detect(function (err, results) {
2626
if (err) {

test/test-simulator.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -166,8 +166,8 @@ describe('simulator', function () {
166166
});
167167

168168
it('detect iOS Simulators', function (done) {
169-
this.timeout(8000);
170-
this.slow(2000);
169+
this.timeout(30000);
170+
this.slow(10000);
171171

172172
ioslib.simulator.detect(function (err, results) {
173173
if (err) {

test/test-teams.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,8 @@ describe('teams', function () {
1717
});
1818

1919
it('detect teams', function (done) {
20-
this.timeout(5000);
21-
this.slow(2000);
20+
this.timeout(30000);
21+
this.slow(10000);
2222

2323
ioslib.teams.detect(function (err, results) {
2424
if (err) {

test/test-xcode.js

Lines changed: 78 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,14 +10,29 @@
1010
*/
1111

1212
const
13+
execFile = require('child_process').execFile,
1314
fs = require('fs'),
1415
ioslib = require('..');
1516

17+
function skipUnlessSelectedXcode27(ctx, done, callback) {
18+
execFile('xcodebuild', [ '-version' ], function (err, stdout) {
19+
if (err) {
20+
return done(err);
21+
}
22+
23+
if (!/^Xcode 27\./m.test(stdout)) {
24+
return ctx.skip();
25+
}
26+
27+
callback();
28+
});
29+
}
30+
1631
function checkXcode(xcode) {
1732
should(xcode).be.an.Object;
1833
should(xcode).have.keys('xcodeapp', 'path', 'selected', 'version', 'build',
1934
'supported', 'eulaAccepted', 'sdks', 'sims', 'simDeviceTypes',
20-
'simRuntimes', 'watchos', 'tvos', 'teams', 'executables');
35+
'simRuntimes', 'simDevicePairs', 'watchos', 'tvos', 'teams', 'executables');
2136

2237
should(xcode.xcodeapp).be.a.String;
2338
should(xcode.xcodeapp).not.equal('');
@@ -154,4 +169,66 @@ describe('xcode', function () {
154169
done();
155170
});
156171
});
172+
173+
it('detect should map Xcode 27 simulator device pairs', function (done) {
174+
this.timeout(5000);
175+
this.slow(2000);
176+
177+
skipUnlessSelectedXcode27(this, done, function () {
178+
ioslib.xcode.detect({ bypassCache: true }, function (err, results) {
179+
if (err) {
180+
return done(err);
181+
}
182+
183+
var xcode27 = Object.keys(results.xcode)
184+
.map(function (id) { return results.xcode[id]; })
185+
.filter(function (xc) { return /^27\./.test(xc.version); })
186+
.shift();
187+
188+
if (!xcode27) {
189+
return done();
190+
}
191+
192+
should(xcode27.simDevicePairs).eql({
193+
'26.x': {
194+
'26.x': true,
195+
'27.x': true
196+
},
197+
'27.x': {
198+
'26.x': true,
199+
'27.x': true
200+
}
201+
});
202+
done();
203+
});
204+
});
205+
});
206+
207+
it('detect should use DeviceHub as the Xcode 27 simulator executable', function (done) {
208+
this.timeout(5000);
209+
this.slow(2000);
210+
211+
skipUnlessSelectedXcode27(this, done, function () {
212+
ioslib.xcode.detect({ bypassCache: true }, function (err, results) {
213+
if (err) {
214+
return done(err);
215+
}
216+
217+
var xcode27 = Object.keys(results.xcode)
218+
.map(function (id) { return results.xcode[id]; })
219+
.filter(function (xc) { return /^27\./.test(xc.version); })
220+
.shift();
221+
222+
if (!xcode27) {
223+
return done();
224+
}
225+
226+
var deviceHub = 'DeviceHub.app/Contents/MacOS/DeviceHub';
227+
should(xcode27.executables.simulator).not.equal(null);
228+
should(xcode27.executables.simulator).endWith(deviceHub);
229+
should(xcode27.executables.watchsimulator).equal(xcode27.executables.simulator);
230+
done();
231+
});
232+
});
233+
});
157234
});

0 commit comments

Comments
 (0)