Skip to content

Commit 9e45c5d

Browse files
committed
Abstract the base service and provide platform dependent implementations so that we could delete some platform specific ifs
1 parent dd27d53 commit 9e45c5d

12 files changed

+438
-39
lines changed

lib/bootstrap.ts

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,6 @@ $injector.require("androidProjectService", "./services/android-project-service")
1212
$injector.require("iOSProjectService", "./services/ios-project-service");
1313

1414
$injector.require("cocoapodsService", "./services/cocoapods-service");
15-
$injector.require("liveSyncServiceBase", "./services/livesync/livesync-service-base");
1615

1716
$injector.require("projectTemplatesService", "./services/project-templates-service");
1817
$injector.require("projectNameService", "./services/project-name-service");
@@ -105,8 +104,10 @@ $injector.requireCommand("platform|clean", "./commands/platform-clean");
105104

106105
$injector.requireCommand("livesync", "./commands/livesync");
107106
$injector.require("usbLiveSyncService", "./services/livesync/livesync-service"); // The name is used in https://github.com/NativeScript/nativescript-dev-typescript
108-
$injector.require("iosLiveSyncServiceLocator", "./services/livesync/ios-livesync-service");
109-
$injector.require("androidLiveSyncServiceLocator", "./services/livesync/android-livesync-service");
107+
$injector.require("iosPlatformLiveSyncServiceLocator", "./services/livesync/ios-platform-livesync-service");
108+
$injector.require("iosLiveSyncServiceLocator", "./services/livesync/ios-device-livesync-service");
109+
$injector.require("androidPlatformLiveSyncServiceLocator", "./services/livesync/android-platform-livesync-service");
110+
$injector.require("androidLiveSyncServiceLocator", "./services/livesync/android-device-livesync-service");
110111

111112
$injector.require("sysInfo", "./sys-info");
112113

lib/declarations.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,11 @@ interface ILiveSyncService {
6161
forceExecuteFullSync: boolean;
6262
}
6363

64+
interface IPlatformLiveSyncService {
65+
fullSync(): IFuture<void>;
66+
partialSync(event: string, filePath: string, dispatcher: IFutureDispatcher): void;
67+
}
68+
6469
interface IOptions extends ICommonOptions {
6570
all: boolean;
6671
baseConfig: string;

lib/providers/livesync-provider.ts

Lines changed: 32 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -3,30 +3,53 @@ import * as temp from "temp";
33

44
export class LiveSyncProvider implements ILiveSyncProvider {
55
constructor(private $androidLiveSyncServiceLocator: {factory: Function},
6+
private $androidPlatformLiveSyncServiceLocator: {factory: Function},
67
private $iosLiveSyncServiceLocator: {factory: Function},
8+
private $iosPlatformLiveSyncServiceLocator: {factory: Function},
79
private $platformService: IPlatformService,
810
private $platformsData: IPlatformsData,
911
private $logger: ILogger,
10-
private $childProcess: IChildProcess) { }
12+
private $childProcess: IChildProcess,
13+
private $devicePlatformsConstants: Mobile.IDevicePlatformsConstants) { }
1114

1215
private static FAST_SYNC_FILE_EXTENSIONS = [".css", ".xml" ,".html"];
1316

17+
private deviceSpecificLiveSyncServicesCache: IDictionary<any> = {};
18+
public get deviceSpecificLiveSyncServices(): IDictionary<any> {
19+
return {
20+
android: (_device: Mobile.IDevice, $injector: IInjector) => {
21+
if(!this.deviceSpecificLiveSyncServicesCache[_device.deviceInfo.identifier]) {
22+
this.deviceSpecificLiveSyncServicesCache[_device.deviceInfo.identifier] = $injector.resolve(this.$androidLiveSyncServiceLocator.factory, {_device: _device});
23+
}
24+
25+
return this.deviceSpecificLiveSyncServicesCache[_device.deviceInfo.identifier];
26+
},
27+
ios: (_device: Mobile.IDevice, $injector: IInjector) => {
28+
if(!this.deviceSpecificLiveSyncServicesCache[_device.deviceInfo.identifier]) {
29+
this.deviceSpecificLiveSyncServicesCache[_device.deviceInfo.identifier] = $injector.resolve(this.$iosLiveSyncServiceLocator.factory, {_device: _device});
30+
}
31+
32+
return this.deviceSpecificLiveSyncServicesCache[_device.deviceInfo.identifier];
33+
}
34+
};
35+
}
36+
1437
private platformSpecificLiveSyncServicesCache: IDictionary<any> = {};
1538
public get platformSpecificLiveSyncServices(): IDictionary<any> {
1639
return {
17-
android: (_device: Mobile.IDevice, $injector: IInjector): IPlatformLiveSyncService => {
18-
if(!this.platformSpecificLiveSyncServicesCache[_device.deviceInfo.identifier]) {
19-
this.platformSpecificLiveSyncServicesCache[_device.deviceInfo.identifier] = $injector.resolve(this.$androidLiveSyncServiceLocator.factory, {_device: _device});
40+
android: (_liveSyncData: ILiveSyncData, $injector: IInjector) => {
41+
if(!this.platformSpecificLiveSyncServicesCache[this.$devicePlatformsConstants.Android]) {
42+
this.platformSpecificLiveSyncServicesCache[this.$devicePlatformsConstants.Android] = $injector.resolve(this.$androidPlatformLiveSyncServiceLocator.factory, { _liveSyncData: _liveSyncData });
2043
}
2144

22-
return this.platformSpecificLiveSyncServicesCache[_device.deviceInfo.identifier];
45+
return this.platformSpecificLiveSyncServicesCache[this.$devicePlatformsConstants.Android];
2346
},
24-
ios: (_device: Mobile.IDevice, $injector: IInjector) => {
25-
if(!this.platformSpecificLiveSyncServicesCache[_device.deviceInfo.identifier]) {
26-
this.platformSpecificLiveSyncServicesCache[_device.deviceInfo.identifier] = $injector.resolve(this.$iosLiveSyncServiceLocator.factory, {_device: _device});
47+
ios: (_liveSyncData: ILiveSyncData, $injector: IInjector) => {
48+
if(!this.platformSpecificLiveSyncServicesCache[this.$devicePlatformsConstants.iOS]) {
49+
this.platformSpecificLiveSyncServicesCache[this.$devicePlatformsConstants.iOS] = $injector.resolve(this.$iosPlatformLiveSyncServiceLocator.factory, { _liveSyncData: _liveSyncData });
2750
}
2851

29-
return this.platformSpecificLiveSyncServicesCache[_device.deviceInfo.identifier];
52+
return this.platformSpecificLiveSyncServicesCache[this.$devicePlatformsConstants.iOS];
3053
}
3154
};
3255
}

lib/services/livesync/android-livesync-service.ts renamed to lib/services/livesync/android-device-livesync-service.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
11
import {DeviceAndroidDebugBridge} from "../../common/mobile/android/device-android-debug-bridge";
22
import {AndroidDeviceHashService} from "../../common/mobile/android/android-device-hash-service";
3-
import {PlatformLiveSyncServiceBase} from "./platform-livesync-service-base";
3+
import {DeviceLiveSyncServiceBase} from "./device-livesync-service-base";
44
import Future = require("fibers/future");
55
import * as helpers from "../../common/helpers";
66
import * as path from "path";
77
import * as net from "net";
88

9-
class AndroidLiveSyncService extends PlatformLiveSyncServiceBase<Mobile.IAndroidDevice> implements IPlatformLiveSyncService {
9+
class AndroidLiveSyncService extends DeviceLiveSyncServiceBase<Mobile.IAndroidDevice> implements IDeviceLiveSyncService {
1010
private static BACKEND_PORT = 18182;
1111

1212
constructor(_device: Mobile.IDevice,
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
import {PlatformLiveSyncServiceBase} from "./platform-livesync-service-base";
2+
3+
class AndroidPlatformLiveSyncService extends PlatformLiveSyncServiceBase {
4+
constructor(_liveSyncData: ILiveSyncData,
5+
protected $devicesService: Mobile.IDevicesService,
6+
protected $mobileHelper: Mobile.IMobileHelper,
7+
protected $logger: ILogger,
8+
protected $options: ICommonOptions,
9+
protected $deviceAppDataFactory: Mobile.IDeviceAppDataFactory,
10+
protected $fs: IFileSystem,
11+
protected $injector: IInjector,
12+
protected $projectFilesManager: IProjectFilesManager,
13+
protected $projectFilesProvider: IProjectFilesProvider,
14+
protected $liveSyncProvider: ILiveSyncProvider) {
15+
super(_liveSyncData, $devicesService, $mobileHelper, $logger, $options, $deviceAppDataFactory, $fs, $injector, $projectFilesManager, $projectFilesProvider, $liveSyncProvider);
16+
}
17+
18+
public fullSync(): IFuture<void> {
19+
return (() => {
20+
let appIdentifier = this.liveSyncData.appIdentifier;
21+
let platform = this.liveSyncData.platform;
22+
let projectFilesPath = this.liveSyncData.projectFilesPath;
23+
let canExecute = this.getCanExecuteAction(platform, appIdentifier);
24+
let action = (device: Mobile.IDevice): IFuture<void> => {
25+
return (() => {
26+
let deviceLiveSyncService = this.resolveDeviceSpecificLiveSyncService(platform, device);
27+
let deviceAppData = this.$deviceAppDataFactory.create(appIdentifier, this.$mobileHelper.normalizePlatformName(platform), device);
28+
29+
deviceLiveSyncService.beforeLiveSyncAction(deviceAppData).wait();;
30+
31+
let installed = this.tryInstallApplication(device, deviceAppData).wait();
32+
let localToDevicePaths = this.$projectFilesManager.createLocalToDevicePaths(deviceAppData, projectFilesPath, null, this.liveSyncData.excludedProjectDirsAndFiles);
33+
34+
if (installed) {
35+
deviceLiveSyncService.afterInstallApplicationAction(deviceAppData, localToDevicePaths).wait();
36+
37+
device.applicationManager.tryStartApplication(deviceAppData.appIdentifier).wait();
38+
} else {
39+
this.transferFiles(deviceAppData, localToDevicePaths, this.liveSyncData.projectFilesPath, true).wait();
40+
this.refreshApplication(deviceAppData, localToDevicePaths, this.liveSyncData.forceExecuteFullSync).wait();
41+
}
42+
}).future<void>()();
43+
};
44+
this.$devicesService.execute(action, canExecute).wait();
45+
}).future<void>()();
46+
}
47+
48+
protected getCanExecuteActionCore(platform: string, appIdentifier: string): (dev: Mobile.IDevice) => boolean {
49+
return (device: Mobile.IDevice) => true;
50+
}
51+
}
52+
53+
$injector.register("androidPlatformLiveSyncServiceLocator", {factory: AndroidPlatformLiveSyncService});
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
export abstract class DeviceLiveSyncServiceBase<T extends Mobile.IDevice> {
2+
protected get device(): T {
3+
return <T>(this._device);
4+
}
5+
6+
constructor(private _device: Mobile.IDevice,
7+
private $liveSyncProvider: ILiveSyncProvider) { }
8+
9+
public refreshApplication(deviceAppData: Mobile.IDeviceAppData, localToDevicePaths: Mobile.ILocalToDevicePathData[], forceExecuteFullSync: boolean): IFuture<void> {
10+
let canExecuteFastSync = !forceExecuteFullSync && !_.some(localToDevicePaths, (localToDevicePath:any) => !this.$liveSyncProvider.canExecuteFastSync(localToDevicePath.getLocalPath(), deviceAppData.platform));
11+
12+
if (canExecuteFastSync) {
13+
return this.reloadPage(deviceAppData);
14+
}
15+
16+
return this.restartApplication(deviceAppData);
17+
}
18+
19+
protected abstract restartApplication(deviceAppData: Mobile.IDeviceAppData): IFuture<void>;
20+
protected abstract reloadPage(deviceAppData: Mobile.IDeviceAppData): IFuture<void>;
21+
}

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
1-
import {PlatformLiveSyncServiceBase} from "./platform-livesync-service-base";
1+
import {DeviceLiveSyncServiceBase} from "./device-livesync-service-base";
22
import * as helpers from "../../common/helpers";
33
import * as net from "net";
44

55
let currentPageReloadId = 0;
66

7-
class IOSLiveSyncService extends PlatformLiveSyncServiceBase<Mobile.IiOSDevice> implements IPlatformLiveSyncService {
7+
class IOSLiveSyncService extends DeviceLiveSyncServiceBase<Mobile.IiOSDevice> implements IDeviceLiveSyncService {
88
private static BACKEND_PORT = 18181;
99
private socket: net.Socket;
1010

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
import {PlatformLiveSyncServiceBase} from "./platform-livesync-service-base";
2+
3+
class IOSPlatformLiveSyncService extends PlatformLiveSyncServiceBase {
4+
constructor(_liveSyncData: ILiveSyncData,
5+
protected $devicesService: Mobile.IDevicesService,
6+
protected $mobileHelper: Mobile.IMobileHelper,
7+
protected $logger: ILogger,
8+
protected $options: ICommonOptions,
9+
protected $deviceAppDataFactory: Mobile.IDeviceAppDataFactory,
10+
protected $fs: IFileSystem,
11+
protected $injector: IInjector,
12+
protected $projectFilesManager: IProjectFilesManager,
13+
protected $projectFilesProvider: IProjectFilesProvider,
14+
protected $liveSyncProvider: ILiveSyncProvider) {
15+
super(_liveSyncData, $devicesService, $mobileHelper, $logger, $options, $deviceAppDataFactory, $fs, $injector, $projectFilesManager, $projectFilesProvider, $liveSyncProvider);
16+
}
17+
18+
public fullSync(): IFuture<void> {
19+
return (() => {
20+
let appIdentifier = this.liveSyncData.appIdentifier;
21+
let platform = this.liveSyncData.platform;
22+
let projectFilesPath = this.liveSyncData.projectFilesPath;
23+
let canExecute = this.getCanExecuteAction(platform, appIdentifier);
24+
25+
let action = (device: Mobile.IDevice): IFuture<void> => {
26+
return (() => {
27+
let deviceAppData = this.$deviceAppDataFactory.create(appIdentifier, this.$mobileHelper.normalizePlatformName(platform), device);
28+
let installed = this.tryInstallApplication(device, deviceAppData).wait();
29+
30+
if(installed) {
31+
device.applicationManager.tryStartApplication(deviceAppData.appIdentifier).wait();
32+
} else {
33+
let localToDevicePaths = this.$projectFilesManager.createLocalToDevicePaths(deviceAppData, projectFilesPath, null, this.liveSyncData.excludedProjectDirsAndFiles);
34+
this.transferFiles(deviceAppData, localToDevicePaths, this.liveSyncData.projectFilesPath, true).wait();
35+
this.refreshApplication(deviceAppData, localToDevicePaths, this.liveSyncData.forceExecuteFullSync).wait();
36+
}
37+
}).future<void>()();
38+
};
39+
this.$devicesService.execute(action, canExecute).wait();
40+
}).future<void>()();
41+
}
42+
43+
protected getCanExecuteActionCore(platform: string, appIdentifier: string): (dev: Mobile.IDevice) => boolean {
44+
if (this.$options.emulator) {
45+
return (device: Mobile.IDevice): boolean => this.$devicesService.isiOSSimulator(device);
46+
} else {
47+
let devices = this.$devicesService.getDevicesForPlatform(platform);
48+
let simulator = _.find(devices, d => this.$devicesService.isiOSSimulator(d));
49+
if (simulator) {
50+
let iOSDevices = _.filter(devices, d => d.deviceInfo.identifier !== simulator.deviceInfo.identifier);
51+
if (iOSDevices && iOSDevices.length) {
52+
let isApplicationInstalledOnSimulator = simulator.applicationManager.isApplicationInstalled(appIdentifier).wait();
53+
let isApplicationInstalledOnAllDevices = _.intersection.apply(null, iOSDevices.map(device => device.applicationManager.isApplicationInstalled(appIdentifier).wait()));
54+
// In case the application is not installed on both device and simulator, syncs only on device.
55+
if (!isApplicationInstalledOnSimulator && !isApplicationInstalledOnAllDevices) {
56+
return (device: Mobile.IDevice): boolean => this.$devicesService.isiOSDevice(device);
57+
}
58+
}
59+
}
60+
}
61+
}
62+
}
63+
64+
$injector.register("iosPlatformLiveSyncServiceLocator", {factory: IOSPlatformLiveSyncService});

lib/services/livesync/livesync-service.ts

Lines changed: 47 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,23 +2,29 @@ import * as constants from "../../constants";
22
import * as helpers from "../../common/helpers";
33
import * as path from "path";
44
import * as semver from "semver";
5+
import * as fiberBootstrap from "../../common/fiber-bootstrap";
6+
7+
let gaze = require("gaze");
58

69
class LiveSyncService implements ILiveSyncService {
710
public forceExecuteFullSync = false;
811
private _isInitialized = false;
912

1013
constructor(private $devicePlatformsConstants: Mobile.IDevicePlatformsConstants,
1114
private $errors: IErrors,
12-
private $liveSyncServiceBase: ILiveSyncServiceBase,
1315
private $platformsData: IPlatformsData,
1416
private $platformService: IPlatformService,
1517
private $projectData: IProjectData,
1618
private $projectDataService: IProjectDataService,
1719
private $prompter: IPrompter,
1820
private $injector: IInjector,
21+
private $liveSyncProvider: ILiveSyncProvider,
1922
private $mobileHelper: Mobile.IMobileHelper,
2023
private $devicesService: Mobile.IDevicesService,
21-
private $options: IOptions) { }
24+
private $options: IOptions,
25+
private $logger: ILogger,
26+
private $dispatcher: IFutureDispatcher,
27+
private $hooksService: IHooksService) { }
2228

2329
private ensureAndroidFrameworkVersion(platformData: IPlatformData): IFuture<void> { // TODO: this can be moved inside command or canExecute function
2430
return (() => {
@@ -93,11 +99,49 @@ class LiveSyncService implements ILiveSyncService {
9399
return liveSyncData;
94100
}
95101

102+
private resolvePlatformLiveSyncBaseService(platform: string, liveSyncData: ILiveSyncData): IPlatformLiveSyncService {
103+
return this.$injector.resolve(this.$liveSyncProvider.platformSpecificLiveSyncServices[platform.toLowerCase()], { _liveSyncData: liveSyncData });
104+
}
105+
96106
@helpers.hook('livesync')
97107
private liveSyncCore(liveSyncData: ILiveSyncData[]): IFuture<void> {
98108
return (() => {
99-
this.$liveSyncServiceBase.sync(liveSyncData).wait();
109+
let watchForChangeActions: ((event: string, filePath: string, dispatcher: IFutureDispatcher) => void)[] = [];
110+
_.each(liveSyncData, (dataItem) => {
111+
let service = this.resolvePlatformLiveSyncBaseService(dataItem.platform, dataItem);
112+
113+
watchForChangeActions.push((event: string, filePath: string, dispatcher: IFutureDispatcher) => service.partialSync(event, filePath, dispatcher));
114+
service.fullSync().wait();
115+
});
116+
117+
if(this.$options.watch) {
118+
this.$hooksService.executeBeforeHooks('watch').wait();
119+
this.partialSync(liveSyncData[0].syncWorkingDirectory, watchForChangeActions);
120+
}
100121
}).future<void>()();
101122
}
123+
124+
private partialSync(syncWorkingDirectory: string, onChangedActions: ((event: string, filePath: string, dispatcher: IFutureDispatcher) => void )[]): void {
125+
let that = this;
126+
127+
gaze("**/*", { cwd: syncWorkingDirectory }, function (err: any, watcher: any) {
128+
this.on('all', (event: string, filePath: string) => {
129+
fiberBootstrap.run(() => {
130+
that.$dispatcher.dispatch(() => (() => {
131+
try {
132+
for (let i = 0; i < onChangedActions.length; i++) {
133+
onChangedActions[i](event, filePath, that.$dispatcher);
134+
}
135+
} catch (err) {
136+
that.$logger.info(`Unable to sync file ${filePath}. Error is:${err.message}`.red.bold);
137+
that.$logger.info("Try saving it again or restart the livesync operation.");
138+
}
139+
}).future<void>()());
140+
});
141+
});
142+
});
143+
144+
this.$dispatcher.run();
145+
}
102146
}
103147
$injector.register("usbLiveSyncService", LiveSyncService);

0 commit comments

Comments
 (0)