Skip to content

Commit 242cd3b

Browse files
Remove ios native dependencies (#2495)
* Remove ios native dependencies We need to remove our native dependencies - ref and ffi because they are casuing problems with each version of NodeJS which has new version of V8 in it. We are using ref and ffi to communicate with iTunes and execute fs and application management operations on iOS devices. This operations are moved in new dependency - device-lib. The device lib has JavaScript wrapper which we use in the CLI. Everything in the ios device file system and ios device application manager is replaced with the exposed methods from the device lib. * Change the notification logic * Post-rebase fixes * Add receive message from ios device socket logic * Fix rebase issues * WIP * Fix tns debug ios issues Since we use the ios-device-lib to connect to the device backend port for debugging, the socket returned from the device can be used only in the ios-device lib. That's why we need to change the logic for creating the Node.js socket in the CLI. The ios-device-lib will expose the device socket on a specific port in the localhost. After we connect to the exposed socket we can continue to work like we used to. * Update ios-devic-lib version
1 parent 6e027e8 commit 242cd3b

9 files changed

+86
-70
lines changed

lib/declarations.ts

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -290,7 +290,7 @@ interface IAndroidToolsInfoData {
290290

291291
interface ISocketProxyFactory {
292292
createTCPSocketProxy(factory: () => any): any;
293-
createWebSocketProxy(factory: () => any): any;
293+
createWebSocketProxy(factory: () => Promise<any>): any;
294294
}
295295

296296
interface IiOSNotification {
@@ -304,11 +304,12 @@ interface IiOSNotification {
304304
}
305305

306306
interface IiOSNotificationService {
307-
awaitNotification(npc: Mobile.INotificationProxyClient, notification: string, timeout: number): Promise<string>;
307+
awaitNotification(deviceIdentifier: string, socket: number, timeout: number): Promise<string>;
308+
postNotification(deviceIdentifier: string, notification: string, commandType?: string): Promise<string>;
308309
}
309310

310311
interface IiOSSocketRequestExecutor {
311-
executeLaunchRequest(device: Mobile.IiOSDevice, timeout: number, readyForAttachTimeout: number, projectId: string, shouldBreak?: boolean): Promise<void>;
312+
executeLaunchRequest(deviceIdentifier: string, timeout: number, readyForAttachTimeout: number, projectId: string, shouldBreak?: boolean): Promise<void>;
312313
executeAttachRequest(device: Mobile.IiOSDevice, timeout: number, projectId: string): Promise<void>;
313314
}
314315

lib/device-sockets/ios/socket-proxy-factory.ts

Lines changed: 6 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -66,8 +66,7 @@ export class SocketProxyFactory implements ISocketProxyFactory {
6666
return server;
6767
}
6868

69-
public createWebSocketProxy(factory: () => net.Socket): ws.Server {
70-
let socketFactory = (callback: (_socket: net.Socket) => void) => helpers.connectEventually(factory, callback);
69+
public createWebSocketProxy(factory: () => Promise<net.Socket>): ws.Server {
7170
// NOTE: We will try to provide command line options to select ports, at least on the localhost.
7271
let localPort = 8080;
7372

@@ -80,13 +79,12 @@ export class SocketProxyFactory implements ISocketProxyFactory {
8079

8180
let server = new ws.Server(<any>{
8281
port: localPort,
83-
verifyClient: (info: any, callback: Function) => {
82+
verifyClient: async (info: any, callback: Function) => {
8483
this.$logger.info("Frontend client connected.");
85-
socketFactory((_socket: any) => {
86-
this.$logger.info("Backend socket created.");
87-
info.req["__deviceSocket"] = _socket;
88-
callback(true);
89-
});
84+
const _socket = await factory();
85+
this.$logger.info("Backend socket created.");
86+
info.req["__deviceSocket"] = _socket;
87+
callback(true);
9088
}
9189
});
9290
server.on("connection", (webSocket) => {

lib/device-sockets/ios/socket-request-executor.ts

Lines changed: 48 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1,26 +1,36 @@
1-
import * as iOSProxyServices from "../../common/mobile/ios/device/ios-proxy-services";
1+
import * as constants from "../../common/constants";
22

33
export class IOSSocketRequestExecutor implements IiOSSocketRequestExecutor {
44
constructor(private $errors: IErrors,
5-
private $injector: IInjector,
65
private $iOSNotification: IiOSNotification,
76
private $iOSNotificationService: IiOSNotificationService,
8-
private $logger: ILogger) { }
7+
private $logger: ILogger,
8+
private $iosDeviceOperations: IIOSDeviceOperations) {
9+
this.$iosDeviceOperations.setShouldDispose(false);
10+
}
911

1012
public async executeAttachRequest(device: Mobile.IiOSDevice, timeout: number, projectId: string): Promise<void> {
11-
let npc = new iOSProxyServices.NotificationProxyClient(device, this.$injector);
13+
const deviceIdentifier = device.deviceInfo.identifier;
14+
15+
const observeNotificationSockets = [
16+
await this.$iOSNotificationService.postNotification(deviceIdentifier, this.$iOSNotification.getAlreadyConnected(projectId), constants.IOS_OBSERVE_NOTIFICATION_COMMAND_TYPE),
17+
await this.$iOSNotificationService.postNotification(deviceIdentifier, this.$iOSNotification.getReadyForAttach(projectId), constants.IOS_OBSERVE_NOTIFICATION_COMMAND_TYPE),
18+
await this.$iOSNotificationService.postNotification(deviceIdentifier, this.$iOSNotification.getAttachAvailable(projectId), constants.IOS_OBSERVE_NOTIFICATION_COMMAND_TYPE)
19+
];
1220

13-
let data = [this.$iOSNotification.getAlreadyConnected(projectId), this.$iOSNotification.getReadyForAttach(projectId), this.$iOSNotification.getAttachAvailable(projectId)]
14-
.map((notification) => this.$iOSNotificationService.awaitNotification(npc, notification, timeout)),
15-
alreadyConnected = data[0],
16-
readyForAttach = data[1],
17-
attachAvailable = data[2];
21+
const observeNotificationPromises = _(observeNotificationSockets)
22+
.uniq()
23+
.map(s => {
24+
return this.$iOSNotificationService.awaitNotification(deviceIdentifier, +s, timeout);
25+
})
26+
.value();
1827

19-
npc.postNotificationAndAttachForData(this.$iOSNotification.getAttachAvailabilityQuery(projectId));
28+
// Trigger the notifications update.
29+
await this.$iOSNotificationService.postNotification(deviceIdentifier, this.$iOSNotification.getAttachAvailabilityQuery(projectId));
2030

2131
let receivedNotification: string;
2232
try {
23-
receivedNotification = await Promise.race([alreadyConnected, readyForAttach, attachAvailable]);
33+
receivedNotification = await Promise.race(observeNotificationPromises);
2434
} catch (e) {
2535
this.$errors.failWithoutHelp(`The application ${projectId} does not appear to be running on ${device.deviceInfo.displayName} or is not built with debugging enabled.`);
2636
}
@@ -30,37 +40,47 @@ export class IOSSocketRequestExecutor implements IiOSSocketRequestExecutor {
3040
this.$errors.failWithoutHelp("A client is already connected.");
3141
break;
3242
case this.$iOSNotification.getAttachAvailable(projectId):
33-
await this.executeAttachAvailable(npc, timeout, projectId);
43+
await this.executeAttachAvailable(deviceIdentifier, projectId, timeout);
3444
break;
3545
case this.$iOSNotification.getReadyForAttach(projectId):
3646
break;
47+
default:
48+
this.$logger.trace("Response from attach availability query:");
49+
this.$logger.trace(receivedNotification);
50+
this.$errors.failWithoutHelp("No notification received while executing attach request.");
3751
}
3852
}
3953

40-
public async executeLaunchRequest(device: Mobile.IiOSDevice, timeout: number, readyForAttachTimeout: number, projectId: string, shouldBreak?: boolean): Promise<void> {
41-
let npc = new iOSProxyServices.NotificationProxyClient(device, this.$injector);
42-
54+
public async executeLaunchRequest(deviceIdentifier: string, timeout: number, readyForAttachTimeout: number, projectId: string, shouldBreak?: boolean): Promise<void> {
4355
try {
44-
await this.$iOSNotificationService.awaitNotification(npc, this.$iOSNotification.getAppLaunching(projectId), timeout);
45-
process.nextTick(() => {
46-
if (shouldBreak) {
47-
npc.postNotificationAndAttachForData(this.$iOSNotification.getWaitForDebug(projectId));
48-
}
56+
const appLaunchingSocket = await this.$iOSNotificationService.postNotification(deviceIdentifier, this.$iOSNotification.getAppLaunching(projectId), constants.IOS_OBSERVE_NOTIFICATION_COMMAND_TYPE);
57+
await this.$iOSNotificationService.awaitNotification(deviceIdentifier, +appLaunchingSocket, timeout);
58+
59+
if (shouldBreak) {
60+
await this.$iOSNotificationService.postNotification(deviceIdentifier, this.$iOSNotification.getWaitForDebug(projectId));
61+
}
4962

50-
npc.postNotificationAndAttachForData(this.$iOSNotification.getAttachRequest(projectId));
51-
});
63+
// We need to send the ObserveNotification ReadyForAttach before we post the AttachRequest.
64+
const readyForAttachSocket = await this.$iOSNotificationService.postNotification(deviceIdentifier, this.$iOSNotification.getReadyForAttach(projectId), constants.IOS_OBSERVE_NOTIFICATION_COMMAND_TYPE);
65+
const readyForAttachPromise = this.$iOSNotificationService.awaitNotification(deviceIdentifier, +readyForAttachSocket, readyForAttachTimeout);
5266

53-
await this.$iOSNotificationService.awaitNotification(npc, this.$iOSNotification.getReadyForAttach(projectId), readyForAttachTimeout);
67+
await this.$iOSNotificationService.postNotification(deviceIdentifier, this.$iOSNotification.getAttachRequest(projectId));
68+
await readyForAttachPromise;
5469
} catch (e) {
55-
this.$logger.trace(`Timeout error: ${e}`);
56-
this.$errors.failWithoutHelp("Timeout waiting for response from NativeScript runtime.");
70+
this.$logger.trace("Launch request error:");
71+
this.$logger.trace(e);
72+
this.$errors.failWithoutHelp("Error while waiting for response from NativeScript runtime.");
5773
}
5874
}
5975

60-
private async executeAttachAvailable(npc: Mobile.INotificationProxyClient, timeout: number, projectId: string): Promise<void> {
61-
process.nextTick(() => npc.postNotificationAndAttachForData(this.$iOSNotification.getAttachRequest(projectId)));
76+
private async executeAttachAvailable(deviceIdentifier: string, projectId: string, timeout: number): Promise<void> {
6277
try {
63-
await this.$iOSNotificationService.awaitNotification(npc, this.$iOSNotification.getReadyForAttach(projectId), timeout);
78+
// We should create this promise here because we need to send the ObserveNotification on the device
79+
// before we send the PostNotification.
80+
const readyForAttachSocket = await this.$iOSNotificationService.postNotification(deviceIdentifier, this.$iOSNotification.getReadyForAttach(projectId), constants.IOS_OBSERVE_NOTIFICATION_COMMAND_TYPE);
81+
const readyForAttachPromise = this.$iOSNotificationService.awaitNotification(deviceIdentifier, +readyForAttachSocket, timeout);
82+
await this.$iOSNotificationService.postNotification(deviceIdentifier, this.$iOSNotification.getAttachRequest(projectId));
83+
await readyForAttachPromise;
6484
} catch (e) {
6585
this.$errors.failWithoutHelp(`The application ${projectId} timed out when performing the socket handshake.`);
6686
}

lib/services/ios-debug-service.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -158,7 +158,7 @@ class IOSDebugService implements IDebugService {
158158

159159
private async debugBrkCore(device: Mobile.IiOSDevice, projectData: IProjectData, shouldBreak?: boolean): Promise<void> {
160160
let timeout = this.$utils.getMilliSecondsTimeout(TIMEOUT_SECONDS);
161-
await this.$iOSSocketRequestExecutor.executeLaunchRequest(device, timeout, timeout, projectData.projectId, shouldBreak);
161+
await this.$iOSSocketRequestExecutor.executeLaunchRequest(device.deviceInfo.identifier, timeout, timeout, projectData.projectId, shouldBreak);
162162
await this.wireDebuggerClient(projectData, device);
163163
}
164164

@@ -174,8 +174,8 @@ class IOSDebugService implements IDebugService {
174174
}
175175

176176
private async wireDebuggerClient(projectData: IProjectData, device?: Mobile.IiOSDevice): Promise<void> {
177-
let factory = () => {
178-
let socket = device ? device.connectToPort(inspectorBackendPort) : net.connect(inspectorBackendPort);
177+
const factory = async () => {
178+
let socket = device ? await device.connectToPort(inspectorBackendPort) : net.connect(inspectorBackendPort);
179179
this._sockets.push(socket);
180180
return socket;
181181
};
Lines changed: 18 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,25 @@
1-
export class IOSNotificationService implements IiOSNotificationService {
2-
public async awaitNotification(npc: Mobile.INotificationProxyClient, notification: string, timeout: number): Promise<string> {
3-
return new Promise<string>((resolve, reject) => {
4-
5-
let timeoutToken = setTimeout(() => {
6-
detachObserver();
7-
reject(new Error(`Timeout receiving ${notification} notification.`));
8-
}, timeout);
1+
import * as constants from "../common/constants";
92

10-
function notificationObserver(_notification: string) {
11-
clearTimeout(timeoutToken);
12-
detachObserver();
13-
resolve(_notification);
14-
}
3+
export class IOSNotificationService implements IiOSNotificationService {
4+
constructor(private $iosDeviceOperations: IIOSDeviceOperations) { }
155

16-
function detachObserver() {
17-
process.nextTick(() => npc.removeObserver(notification, notificationObserver));
18-
}
6+
public async awaitNotification(deviceIdentifier: string, socket: number, timeout: number): Promise<string> {
7+
const notificationResponse = await this.$iosDeviceOperations.awaitNotificationResponse([{
8+
deviceId: deviceIdentifier,
9+
socket: socket,
10+
timeout: timeout,
11+
responseCommandType: constants.IOS_RELAY_NOTIFICATION_COMMAND_TYPE,
12+
responsePropertyName: "Name"
13+
}]);
1914

20-
npc.addObserver(notification, notificationObserver);
15+
return _.first(notificationResponse[deviceIdentifier]).response;
16+
}
2117

22-
});
18+
public async postNotification(deviceIdentifier: string, notification: string, commandType?: string): Promise<string> {
19+
commandType = commandType || constants.IOS_POST_NOTIFICATION_COMMAND_TYPE;
20+
const response = await this.$iosDeviceOperations.postNotification([{ deviceId: deviceIdentifier, commandType: commandType, notificationName: notification }]);
21+
return _.first(response[deviceIdentifier]).response;
2322
}
2423
}
24+
2525
$injector.register("iOSNotificationService", IOSNotificationService);

lib/services/livesync/ios-device-livesync-service.ts

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -10,19 +10,18 @@ class IOSLiveSyncService implements INativeScriptDeviceLiveSyncService {
1010
private socket: net.Socket;
1111
private device: Mobile.IiOSDevice;
1212

13-
constructor(_device: Mobile.IDevice,
13+
constructor(_device: Mobile.IiOSDevice,
1414
private $iOSSocketRequestExecutor: IiOSSocketRequestExecutor,
1515
private $iOSNotification: IiOSNotification,
1616
private $iOSEmulatorServices: Mobile.IiOSSimulatorService,
17-
private $injector: IInjector,
1817
private $logger: ILogger,
1918
private $options: IOptions,
2019
private $iOSDebugService: IDebugService,
2120
private $fs: IFileSystem,
2221
private $liveSyncProvider: ILiveSyncProvider,
2322
private $processService: IProcessService) {
2423

25-
this.device = <Mobile.IiOSDevice>(_device);
24+
this.device = _device;
2625
}
2726

2827
public get debugService(): IDebugService {
@@ -49,7 +48,7 @@ class IOSLiveSyncService implements INativeScriptDeviceLiveSyncService {
4948
} else {
5049
let timeout = 9000;
5150
await this.$iOSSocketRequestExecutor.executeAttachRequest(this.device, timeout, projectId);
52-
this.socket = this.device.connectToPort(IOSLiveSyncService.BACKEND_PORT);
51+
this.socket = await this.device.connectToPort(IOSLiveSyncService.BACKEND_PORT);
5352
}
5453

5554
this.attachEventHandlers();
@@ -88,8 +87,7 @@ class IOSLiveSyncService implements INativeScriptDeviceLiveSyncService {
8887
}
8988

9089
private async restartApplication(deviceAppData: Mobile.IDeviceAppData): Promise<void> {
91-
let projectData: IProjectData = this.$injector.resolve("projectData");
92-
return this.device.applicationManager.restartApplication(deviceAppData.appIdentifier, projectData.projectName);
90+
return this.device.applicationManager.restartApplication(deviceAppData.appIdentifier);
9391
}
9492

9593
private async reloadPage(deviceAppData: Mobile.IDeviceAppData, localToDevicePaths: Mobile.ILocalToDevicePathData[]): Promise<void> {

package.json

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -37,12 +37,12 @@
3737
"colors": "1.1.2",
3838
"email-validator": "1.0.4",
3939
"esprima": "2.7.0",
40-
"ffi": "https://github.com/icenium/node-ffi/tarball/v2.0.0.5",
4140
"filesize": "3.1.2",
4241
"gaze": "1.1.0",
4342
"glob": "^7.0.3",
4443
"iconv-lite": "0.4.11",
4544
"inquirer": "0.9.0",
45+
"ios-device-lib": "~0.3.0",
4646
"ios-mobileprovision-finder": "1.0.9",
4747
"ios-sim-portable": "~2.0.0",
4848
"lockfile": "1.0.1",
@@ -62,8 +62,6 @@
6262
"plistlib": "0.2.1",
6363
"progress-stream": "1.1.1",
6464
"properties-parser": "0.2.3",
65-
"ref": "https://github.com/icenium/ref/tarball/v1.3.2.3",
66-
"ref-struct": "https://github.com/telerik/ref-struct/tarball/v1.0.2.5",
6765
"semver": "5.0.1",
6866
"shelljs": "0.7.6",
6967
"source-map": "0.5.6",

test/ios-project-service.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,7 @@ function createTestInjector(projectPath: string, projectName: string): IInjector
7979
testInjector.register("utils", Utils);
8080
testInjector.register("iTunesValidator", {});
8181
testInjector.register("xcprojService", {});
82+
testInjector.register("iosDeviceOperations", {});
8283
testInjector.register("pluginVariablesService", PluginVariablesService);
8384
testInjector.register("pluginVariablesHelper", PluginVariablesHelper);
8485
testInjector.register("androidProcessService", {});

0 commit comments

Comments
 (0)