Skip to content

Commit 0579ec7

Browse files
Debug fixes when CLI is used as a library (#3113)
* Fix UnhandledRejection error when failed to start application on iOS during debug In case we are unable to start application on iOS device just before attaching a debugger, CLI will raise unhandled rejection. The problem is that we want to start two actions in parallel - start of application and attaching a debugger. We do not await the start application and when an error is raised in it, Unhandled Rejection is raised. In order to prevent this, use Promise.all - this way in case any of the operations fail, we'll receive it as error in the same method. * Fix attaching multiple times on deviceLost event In LiveSync service we attach to deviceLost event in order to stop LiveSync operation in case device is detached. However we are attaching it on every change, so at some point CLI prints warning: `Warning: Possible EventEmitter memory leak detected. 11 deviceLost listeners added. Use emitter.setMaxListeners() to increase limit ` Fix this by attaching only once. * Fix application is not started on iOS Simulator every other time In case you are using iOS Simulator with version < 10, every second LiveSync operation does not start the app on device. The problem is that calling `simctl terminate` is not killing the app immediately. However the action is executed, so we call start application. After its been called, the terminate succeeds and kills the app. So the app is not running. When a change is applied we detect app is not running and just start it. So it works. Next change calls terminate and again the app seems like it has not been started. The fix is in ios-sim-portable, so update its version. * Fix executing of deploy/start app on device, which is not passed to the method In PlatformService we create a canExecute method that does not work correctly in cases when CLI is used as a library. The problem is that in this case methods like `deployPlatform` and `startApplication` receive the device on which to execute the action through their arguments. However, the canExecute method relies on the `devicesService.getDeviceByDeviceOption()` method, which works with the `$options.device`. The last is never set in cases when CLI is used as a library. So the canExecute is changed and now it will work only in case it receives deviceIdentifier as argument. This requires changes in `startApplication` and `deployPlatform` methods, so they will send the deviceIdentifier to canExecute method.
1 parent 7889c55 commit 0579ec7

File tree

5 files changed

+44
-22
lines changed

5 files changed

+44
-22
lines changed

lib/services/ios-debug-service.ts

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -160,7 +160,7 @@ export class IOSDebugService extends DebugServiceBase implements IPlatformDebugS
160160

161161
private async deviceDebugBrk(debugData: IDebugData, debugOptions: IDebugOptions): Promise<string> {
162162
await this.$devicesService.initialize({ platform: this.platform, deviceId: debugData.deviceIdentifier });
163-
const action = async (device: iOSDevice.IOSDevice) => {
163+
const action = async (device: iOSDevice.IOSDevice): Promise<string> => {
164164
if (device.isEmulator) {
165165
return await this.emulatorDebugBrk(debugData, debugOptions);
166166
}
@@ -170,14 +170,13 @@ export class IOSDebugService extends DebugServiceBase implements IPlatformDebugS
170170
emulator: debugOptions.emulator,
171171
justlaunch: debugOptions.justlaunch
172172
};
173-
// we intentionally do not wait on this here, because if we did, we'd miss the AppLaunching notification
174-
const startApplicationAction = this.$platformService.startApplication(this.platform, runOptions, debugData.applicationIdentifier);
175173

176-
const result = await this.debugBrkCore(device, debugData, debugOptions);
174+
const promisesResults = await Promise.all<any>([
175+
this.$platformService.startApplication(this.platform, runOptions, debugData.applicationIdentifier),
176+
this.debugBrkCore(device, debugData, debugOptions)
177+
]);
177178

178-
await startApplicationAction;
179-
180-
return result;
179+
return _.last(promisesResults);
181180
};
182181

183182
const deviceActionResult = await this.$devicesService.execute(action, this.getCanExecuteAction(debugData.deviceIdentifier));

lib/services/livesync/livesync-service.ts

Lines changed: 20 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,9 @@ import { EOL } from "os";
44
import { EventEmitter } from "events";
55
import { hook } from "../../common/helpers";
66
import { APP_FOLDER_NAME, PACKAGE_JSON_FILE_NAME, LiveSyncTrackActionNames, USER_INTERACTION_NEEDED_EVENT_NAME, DEBUGGER_ATTACHED_EVENT_NAME, DEBUGGER_DETACHED_EVENT_NAME, TrackActionNames } from "../../constants";
7-
import { FileExtensions, DeviceTypes } from "../../common/constants";
7+
import { FileExtensions, DeviceTypes, DeviceDiscoveryEventNames } from "../../common/constants";
8+
import { cache } from "../../common/decorators";
9+
810
const deviceDescriptorPrimaryKey = "identifier";
911

1012
const LiveSyncEvents = {
@@ -473,6 +475,8 @@ export class LiveSyncService extends EventEmitter implements IDebugLiveSyncServi
473475
// Execute the action only on the deviceDescriptors passed to initialSync.
474476
// In case where we add deviceDescriptors to already running application, we've already executed initialSync for them.
475477
await this.addActionToChain(projectData.projectDir, () => this.$devicesService.execute(deviceAction, (device: Mobile.IDevice) => _.some(deviceDescriptors, deviceDescriptor => deviceDescriptor.identifier === device.deviceInfo.identifier)));
478+
479+
this.attachDeviceLostHandler();
476480
}
477481

478482
private getDefaultLatestAppPackageInstalledSettings(): ILatestAppPackageInstalledSettings {
@@ -632,13 +636,24 @@ export class LiveSyncService extends EventEmitter implements IDebugLiveSyncServi
632636
this.stopLiveSync(projectDir);
633637
});
634638
});
635-
636-
this.$devicesService.on("deviceLost", async (device: Mobile.IDevice) => {
637-
await this.stopLiveSync(projectData.projectDir, [device.deviceInfo.identifier]);
638-
});
639639
}
640640
}
641641

642+
@cache()
643+
private attachDeviceLostHandler(): void {
644+
this.$devicesService.on(DeviceDiscoveryEventNames.DEVICE_LOST, async (device: Mobile.IDevice) => {
645+
this.$logger.trace(`Received ${DeviceDiscoveryEventNames.DEVICE_LOST} event in LiveSync service for ${device.deviceInfo.identifier}. Will stop LiveSync operation for this device.`);
646+
647+
for (const projectDir in this.liveSyncProcessesInfo) {
648+
try {
649+
await this.stopLiveSync(projectDir, [device.deviceInfo.identifier]);
650+
} catch (err) {
651+
this.$logger.warn(`Unable to stop LiveSync operation for ${device.deviceInfo.identifier}.`, err);
652+
}
653+
}
654+
});
655+
}
656+
642657
private async addActionToChain<T>(projectDir: string, action: () => Promise<T>): Promise<T> {
643658
const liveSyncInfo = this.liveSyncProcessesInfo[projectDir];
644659
if (liveSyncInfo) {

lib/services/platform-service.ts

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -587,6 +587,11 @@ export class PlatformService extends EventEmitter implements IPlatformService {
587587
await this.trackActionForPlatform({ action: constants.TrackActionNames.Deploy, platform: device.deviceInfo.platform, isForDevice: !device.isEmulator, deviceOsVersion: device.deviceInfo.version });
588588
};
589589

590+
if (deployOptions.device) {
591+
const device = await this.$devicesService.getDevice(deployOptions.device);
592+
deployOptions.device = device.deviceInfo.identifier;
593+
}
594+
590595
await this.$devicesService.execute(action, this.getCanExecuteAction(platform, deployOptions));
591596
}
592597

@@ -599,6 +604,12 @@ export class PlatformService extends EventEmitter implements IPlatformService {
599604
};
600605

601606
await this.$devicesService.initialize({ platform: platform, deviceId: runOptions.device });
607+
608+
if (runOptions.device) {
609+
const device = await this.$devicesService.getDevice(runOptions.device);
610+
runOptions.device = device.deviceInfo.identifier;
611+
}
612+
602613
await this.$devicesService.execute(action, this.getCanExecuteAction(platform, runOptions));
603614
}
604615

@@ -715,10 +726,7 @@ export class PlatformService extends EventEmitter implements IPlatformService {
715726
private getCanExecuteAction(platform: string, options: IDeviceEmulator): any {
716727
const canExecute = (currentDevice: Mobile.IDevice): boolean => {
717728
if (options.device && currentDevice && currentDevice.deviceInfo) {
718-
const device = this.$devicesService.getDeviceByDeviceOption();
719-
if (device && device.deviceInfo) {
720-
return currentDevice.deviceInfo.identifier === device.deviceInfo.identifier;
721-
}
729+
return currentDevice.deviceInfo.identifier === options.device;
722730
}
723731

724732
if (this.$mobileHelper.isiOSPlatform(platform) && this.$hostInfo.isDarwin) {

npm-shrinkwrap.json

Lines changed: 5 additions & 5 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@
4646
"inquirer": "0.9.0",
4747
"ios-device-lib": "0.4.9",
4848
"ios-mobileprovision-finder": "1.0.10",
49-
"ios-sim-portable": "3.1.1",
49+
"ios-sim-portable": "3.1.2",
5050
"lockfile": "1.0.3",
5151
"lodash": "4.13.1",
5252
"log4js": "1.0.1",

0 commit comments

Comments
 (0)