From 7558ac983d7bd798ee34b12dfd629e587292ba31 Mon Sep 17 00:00:00 2001 From: rosen-vladimirov Date: Wed, 26 Jul 2017 00:41:48 +0300 Subject: [PATCH] Fix LiveSync on iOS when app is uninstalled manually Whenever application is reinstalled on iOS device, we have to execute full sync operation due to iOS runtime requirements. However, in case the application is uninstalled manually during LiveSync, CLI will reinstall it, but the code that checks if a full sync is required will return false. The problem is that the `isRebuilt` property (renamed to `isReinstalled` now) really checks if the application has been rebuild during current check and expectedly this returns false. In order to fix this, change the return type of `ensureLatestAppPackageIsInstalledOnDevice`, so it will give the required information if a full sync should be executed. Also improve the information that we emit in liveSyncExecuted event: * add new property - `isFullSync` - it can be used to indicate the current operation * ensure the `syncedFiles` property contains the really synced files - for Android devices the full sync is not always a full sync - it checks the shasums of files and uploads only the modified ones. At the moment we've been returning all project files in the array, even when we upload only a few (or event none) of them. In order to fix this respect the return type of the `transferDirectory` method and use it in LiveSync operations. --- PublicAPI.md | 1 + lib/common | 2 +- lib/definitions/livesync.d.ts | 12 +++++++++++- lib/services/livesync/ios-livesync-service.ts | 2 +- lib/services/livesync/livesync-service.ts | 17 +++++++++++------ .../livesync/platform-livesync-service-base.ts | 15 +++++++++------ 6 files changed, 34 insertions(+), 15 deletions(-) diff --git a/PublicAPI.md b/PublicAPI.md index ce9e13e09c..0164703980 100644 --- a/PublicAPI.md +++ b/PublicAPI.md @@ -635,6 +635,7 @@ tns.liveSyncService.on("liveSyncStarted", data => { * Full paths to files synced during the operation. In case the `syncedFiles.length` is 0, the operation is "fullSync" (i.e. all project files are synced). */ syncedFiles: string[]; + isFullSync: boolean; } ``` diff --git a/lib/common b/lib/common index 0ff4e5d858..359f7508cb 160000 --- a/lib/common +++ b/lib/common @@ -1 +1 @@ -Subproject commit 0ff4e5d8583f48f702d9a0c3c13c996f14b9694a +Subproject commit 359f7508cbf174452c5afa213337805e1ab50860 diff --git a/lib/definitions/livesync.d.ts b/lib/definitions/livesync.d.ts index 11dea74d7e..6f05f94aeb 100644 --- a/lib/definitions/livesync.d.ts +++ b/lib/definitions/livesync.d.ts @@ -149,6 +149,16 @@ interface IEnsureLatestAppPackageIsInstalledOnDeviceOptions { modifiedFiles?: string[]; } +/** + * Describes the action that has been executed during ensureLatestAppPackageIsInstalledOnDevice execution. + */ +interface IAppInstalledOnDeviceResult { + /** + * Defines if the app has been installed on device from the ensureLatestAppPackageIsInstalledOnDevice method. + */ + appInstalled: boolean; +} + /** * Describes LiveSync operations. */ @@ -187,7 +197,7 @@ interface ILiveSyncWatchInfo { projectData: IProjectData; filesToRemove: string[]; filesToSync: string[]; - isRebuilt: boolean; + isReinstalled: boolean; syncAllFiles: boolean; useLiveEdit?: boolean; } diff --git a/lib/services/livesync/ios-livesync-service.ts b/lib/services/livesync/ios-livesync-service.ts index 3efc0bb3e1..da1e921fe0 100644 --- a/lib/services/livesync/ios-livesync-service.ts +++ b/lib/services/livesync/ios-livesync-service.ts @@ -59,7 +59,7 @@ export class IOSLiveSyncService extends PlatformLiveSyncServiceBase implements I } public liveSyncWatchAction(device: Mobile.IDevice, liveSyncInfo: ILiveSyncWatchInfo): Promise { - if (liveSyncInfo.isRebuilt) { + if (liveSyncInfo.isReinstalled) { // In this case we should execute fullsync because iOS Runtime requires the full content of app dir to be extracted in the root of sync dir. return this.fullSync({ projectData: liveSyncInfo.projectData, device, syncAllFiles: liveSyncInfo.syncAllFiles, watch: true }); } else { diff --git a/lib/services/livesync/livesync-service.ts b/lib/services/livesync/livesync-service.ts index 8816c5686a..e135a871ef 100644 --- a/lib/services/livesync/livesync-service.ts +++ b/lib/services/livesync/livesync-service.ts @@ -109,7 +109,8 @@ export class LiveSyncService extends EventEmitter implements ILiveSyncService { projectDir: projectData.projectDir, applicationIdentifier: projectData.projectId, syncedFiles: liveSyncResultInfo.modifiedFilesData.map(m => m.getLocalPath()), - deviceIdentifier: liveSyncResultInfo.deviceAppData.device.deviceInfo.identifier + deviceIdentifier: liveSyncResultInfo.deviceAppData.device.deviceInfo.identifier, + isFullSync: liveSyncResultInfo.isFullSync }); this.$logger.info(`Successfully synced application ${liveSyncResultInfo.deviceAppData.appIdentifier} on device ${liveSyncResultInfo.deviceAppData.device.deviceInfo.identifier}.`); @@ -154,8 +155,9 @@ export class LiveSyncService extends EventEmitter implements ILiveSyncService { throw new Error(`Invalid platform ${platform}. Supported platforms are: ${this.$mobileHelper.platformNames.join(", ")}`); } - private async ensureLatestAppPackageIsInstalledOnDevice(options: IEnsureLatestAppPackageIsInstalledOnDeviceOptions, nativePrepare?: INativePrepare): Promise { + private async ensureLatestAppPackageIsInstalledOnDevice(options: IEnsureLatestAppPackageIsInstalledOnDeviceOptions, nativePrepare?: INativePrepare): Promise { const platform = options.device.deviceInfo.platform; + const appInstalledOnDeviceResult: IAppInstalledOnDeviceResult = { appInstalled: false }; if (options.preparedPlatforms.indexOf(platform) === -1) { options.preparedPlatforms.push(platform); // TODO: Pass provision and sdk as a fifth argument here @@ -167,7 +169,8 @@ export class LiveSyncService extends EventEmitter implements ILiveSyncService { const buildResult = await this.installedCachedAppPackage(platform, options); if (buildResult) { - return; + appInstalledOnDeviceResult.appInstalled = true; + return appInstalledOnDeviceResult; } // TODO: Pass provision and sdk as a fifth argument here @@ -176,7 +179,6 @@ export class LiveSyncService extends EventEmitter implements ILiveSyncService { let action = LiveSyncTrackActionNames.LIVESYNC_OPERATION; if (shouldBuild) { pathToBuildItem = await options.deviceBuildInfoDescriptor.buildAction(); - // Is it possible to return shouldBuild for two devices? What about android device and android emulator? options.rebuiltInformation.push({ isEmulator: options.device.isEmulator, platform, pathToBuildItem }); action = LiveSyncTrackActionNames.LIVESYNC_OPERATION_BUILD; } @@ -186,7 +188,10 @@ export class LiveSyncService extends EventEmitter implements ILiveSyncService { const shouldInstall = await this.$platformService.shouldInstall(options.device, options.projectData, options.deviceBuildInfoDescriptor.outputPath); if (shouldInstall) { await this.$platformService.installApplication(options.device, { release: false }, options.projectData, pathToBuildItem, options.deviceBuildInfoDescriptor.outputPath); + appInstalledOnDeviceResult.appInstalled = true; } + + return appInstalledOnDeviceResult; } private async trackAction(action: string, platform: string, options: IEnsureLatestAppPackageIsInstalledOnDeviceOptions): Promise { @@ -331,7 +336,7 @@ export class LiveSyncService extends EventEmitter implements ILiveSyncService { const liveSyncProcessInfo = this.liveSyncProcessesInfo[projectData.projectDir]; const deviceBuildInfoDescriptor = _.find(liveSyncProcessInfo.deviceDescriptors, dd => dd.identifier === device.deviceInfo.identifier); - await this.ensureLatestAppPackageIsInstalledOnDevice({ + const appInstalledOnDeviceResult = await this.ensureLatestAppPackageIsInstalledOnDevice({ device, preparedPlatforms, rebuiltInformation, @@ -346,7 +351,7 @@ export class LiveSyncService extends EventEmitter implements ILiveSyncService { projectData, filesToRemove: currentFilesToRemove, filesToSync: currentFilesToSync, - isRebuilt: !!_.find(rebuiltInformation, info => info.isEmulator === device.isEmulator && info.platform === device.deviceInfo.platform), + isReinstalled: appInstalledOnDeviceResult.appInstalled, syncAllFiles: liveSyncData.watchAllFiles, useLiveEdit: liveSyncData.useLiveEdit }; diff --git a/lib/services/livesync/platform-livesync-service-base.ts b/lib/services/livesync/platform-livesync-service-base.ts index 04a3883f20..c8beb26240 100644 --- a/lib/services/livesync/platform-livesync-service-base.ts +++ b/lib/services/livesync/platform-livesync-service-base.ts @@ -44,10 +44,10 @@ export abstract class PlatformLiveSyncServiceBase { const projectFilesPath = path.join(platformData.appDestinationDirectoryPath, APP_FOLDER_NAME); const localToDevicePaths = await this.$projectFilesManager.createLocalToDevicePaths(deviceAppData, projectFilesPath, null, []); - await this.transferFiles(deviceAppData, localToDevicePaths, projectFilesPath, true); + const modifiedFilesData = await this.transferFiles(deviceAppData, localToDevicePaths, projectFilesPath, true); return { - modifiedFilesData: localToDevicePaths, + modifiedFilesData, isFullSync: true, deviceAppData }; @@ -96,19 +96,22 @@ export abstract class PlatformLiveSyncServiceBase { return { modifiedFilesData: modifiedLocalToDevicePaths, - isFullSync: liveSyncInfo.isRebuilt, + isFullSync: liveSyncInfo.isReinstalled, deviceAppData }; } - protected async transferFiles(deviceAppData: Mobile.IDeviceAppData, localToDevicePaths: Mobile.ILocalToDevicePathData[], projectFilesPath: string, isFullSync: boolean): Promise { + protected async transferFiles(deviceAppData: Mobile.IDeviceAppData, localToDevicePaths: Mobile.ILocalToDevicePathData[], projectFilesPath: string, isFullSync: boolean): Promise { + let transferredFiles = localToDevicePaths; if (isFullSync) { - await deviceAppData.device.fileSystem.transferDirectory(deviceAppData, localToDevicePaths, projectFilesPath); + transferredFiles = await deviceAppData.device.fileSystem.transferDirectory(deviceAppData, localToDevicePaths, projectFilesPath); } else { await deviceAppData.device.fileSystem.transferFiles(deviceAppData, localToDevicePaths); } - this.logFilesSyncInformation(localToDevicePaths, "Successfully transferred %s.", this.$logger.info); + this.logFilesSyncInformation(transferredFiles, "Successfully transferred %s.", this.$logger.info); + + return transferredFiles; } protected async getAppData(syncInfo: IFullSyncInfo): Promise {