Skip to content

Commit 4a4353f

Browse files
fix: log process is not started on getDeviceLogProcess
In some cases (especially with Xcode 10), calling getDeviceLogProcess returns a childProcess for the logs, that is not started yet. This breaks `tns debug ios --start` in CLI as the logs are not sent to the CLI immediately and it fails to find the port for debugging.
1 parent 3720dcd commit 4a4353f

File tree

1 file changed

+55
-14
lines changed

1 file changed

+55
-14
lines changed

lib/iphone-simulator-xcode-simctl.ts

Lines changed: 55 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -81,7 +81,7 @@ export class XCodeSimctlSimulator extends IPhoneSimulatorNameGetter implements I
8181
}
8282
}
8383

84-
public uninstallApplication(deviceId: string, appIdentifier: string): Promise<void> {
84+
public uninstallApplication(deviceId: string, appIdentifier: string): Promise<void> {
8585
return this.simctl.uninstall(deviceId, appIdentifier, { skipError: true });
8686
}
8787

@@ -122,22 +122,63 @@ export class XCodeSimctlSimulator extends IPhoneSimulatorNameGetter implements I
122122
}
123123

124124
public async getDeviceLogProcess(deviceId: string, predicate?: string): Promise<child_process.ChildProcess> {
125-
if (!this.isDeviceLogOperationStarted) {
126-
const device = await this.getDeviceFromIdentifier(deviceId);
127-
const deviceVersion = device ? device.runtimeVersion : "";
128-
const majorVersion = deviceVersion.split(".")[0];
125+
const device = await this.getDeviceFromIdentifier(deviceId);
126+
return new Promise<child_process.ChildProcess>((resolve, reject) => {
127+
let timer: NodeJS.Timer;
128+
let isFulfilled = false;
129+
130+
const fulfillSafe = (data?: Error | string) => {
131+
if (!isFulfilled) {
132+
isFulfilled = true;
133+
if (data instanceof Error) {
134+
reject(data);
135+
} else {
136+
resolve(this.deviceLogChildProcess);
137+
}
138+
}
129139

130-
if (majorVersion && parseInt(majorVersion) >= 11) {
131-
this.deviceLogChildProcess = this.simctl.getLog(deviceId, predicate);
132-
} else {
133-
const logFilePath = path.join(osenv.home(), "Library", "Logs", "CoreSimulator", deviceId, "system.log");
134-
this.deviceLogChildProcess = require("child_process").spawn("tail", ['-f', '-n', '1', logFilePath]);
135-
}
140+
if (timer) {
141+
clearTimeout(timer);
142+
timer = null;
143+
}
136144

137-
this.isDeviceLogOperationStarted = true;
138-
}
145+
if (this.deviceLogChildProcess) {
146+
if (this.deviceLogChildProcess.stdout) {
147+
this.deviceLogChildProcess.stdout.removeListener("data", fulfillSafe);
148+
}
139149

140-
return this.deviceLogChildProcess;
150+
this.deviceLogChildProcess.removeListener("error", fulfillSafe);
151+
}
152+
};
153+
154+
if (!this.isDeviceLogOperationStarted) {
155+
const deviceVersion = device ? device.runtimeVersion : "";
156+
const majorVersion = deviceVersion.split(".")[0];
157+
158+
if (majorVersion && parseInt(majorVersion) >= 11) {
159+
timer = setTimeout(() => {
160+
fulfillSafe();
161+
}, 3000);
162+
163+
// For some reason starting the process takes a lot of time. So wait for the first message on stdout and resolve the promise at this point.
164+
this.deviceLogChildProcess = this.simctl.getLog(deviceId, predicate);
165+
if (this.deviceLogChildProcess.stdout) {
166+
this.deviceLogChildProcess.stdout.once("data", fulfillSafe);
167+
this.deviceLogChildProcess.once("error", fulfillSafe);
168+
} else {
169+
fulfillSafe();
170+
}
171+
} else {
172+
const logFilePath = path.join(osenv.home(), "Library", "Logs", "CoreSimulator", deviceId, "system.log");
173+
this.deviceLogChildProcess = childProcess.spawn("tail", ['-f', '-n', '1', logFilePath]);
174+
fulfillSafe();
175+
}
176+
177+
this.isDeviceLogOperationStarted = true;
178+
} else {
179+
fulfillSafe();
180+
}
181+
});
141182
}
142183

143184
private async getDeviceToRun(options: IOptions, device?: any): Promise<IDevice> {

0 commit comments

Comments
 (0)