diff --git a/lib/bootstrap.ts b/lib/bootstrap.ts index 56d552ba6e..39393ad64c 100644 --- a/lib/bootstrap.ts +++ b/lib/bootstrap.ts @@ -15,6 +15,7 @@ $injector.require("gradleCommandService", "./services/android/gradle-command-ser $injector.require("gradleBuildService", "./services/android/gradle-build-service"); $injector.require("gradleBuildArgsService", "./services/android/gradle-build-args-service"); $injector.require("iOSEntitlementsService", "./services/ios-entitlements-service"); +$injector.require("iOSNativeTargetService", "./services/ios-native-target-service"); $injector.require("iOSExtensionsService", "./services/ios-extensions-service"); $injector.require("iOSWatchAppService", "./services/ios-watch-app-service"); $injector.require("iOSProjectService", "./services/ios-project-service"); diff --git a/lib/definitions/project.d.ts b/lib/definitions/project.d.ts index 56c9e23ee4..aae148252b 100644 --- a/lib/definitions/project.d.ts +++ b/lib/definitions/project.d.ts @@ -458,12 +458,26 @@ interface ICocoaPodsPlatformManager { replacePlatformRow(podfileContent: string, podfilePath: string): { replacedContent: string, podfilePlatformData: IPodfilePlatformData }; } +declare const enum BuildNames { + debug = "Debug", + release = "Release" +} + +interface IXcodeTargetBuildConfigurationProperty { + name: string; + value: any; + buildNames?: BuildNames[]; +} + /** * Describes a service used to add and remove iOS extension */ -interface IIOSExtensionsService { - addExtensionsFromPath(options: IAddExtensionsFromPathOptions): Promise; - removeExtensions(options: IRemoveExtensionsOptions): void; +interface IIOSNativeTargetService { + addTargetToProject(targetRootPath: string, targetFolder: string, targetType: string, project: IXcode.project, platformData: IPlatformData, parentTarget?: string): IXcode.target; + prepareSigning(targetUuids: string[], projectData:IProjectData, projectPath: string): void; + getTargetDirectories(folderPath: string): string[]; + setXcodeTargetBuildConfigurationProperties(properties: IXcodeTargetBuildConfigurationProperty[], targetName: string, project: IXcode.project): void + setConfigurationsFromJsonFile(jsonPath: string, targetUuid: string, targetName: string, project: IXcode.project): void } /** diff --git a/lib/services/ios-extensions-service.ts b/lib/services/ios-extensions-service.ts index fc83e100cf..4786e1dbd8 100644 --- a/lib/services/ios-extensions-service.ts +++ b/lib/services/ios-extensions-service.ts @@ -1,12 +1,11 @@ import * as path from "path"; -import { NativeTargetServiceBase } from "./ios-native-target-service-base"; import { IOSNativeTargetProductTypes, IOSNativeTargetTypes } from "../constants"; -export class IOSExtensionsService extends NativeTargetServiceBase implements IIOSExtensionsService { +export class IOSExtensionsService implements IIOSExtensionsService { constructor(protected $fs: IFileSystem, protected $pbxprojDomXcode: IPbxprojDomXcode, - protected $xcode: IXcode) { - super($fs, $pbxprojDomXcode, $xcode); + protected $xcode: IXcode, + private $iOSNativeTargetService: IIOSNativeTargetService) { } public async addExtensionsFromPath({extensionsFolderPath, projectData, platformData, pbxProjPath}: IAddExtensionsFromPathOptions): Promise { @@ -17,16 +16,16 @@ export class IOSExtensionsService extends NativeTargetServiceBase implements IIO } const project = new this.$xcode.project(pbxProjPath); project.parseSync(); - this.getTargetDirectories(extensionsFolderPath) + this.$iOSNativeTargetService.getTargetDirectories(extensionsFolderPath) .forEach(extensionFolder => { - const target = this.addTargetToProject(extensionsFolderPath, extensionFolder, IOSNativeTargetTypes.appExtension, project, platformData); + const target = this.$iOSNativeTargetService.addTargetToProject(extensionsFolderPath, extensionFolder, IOSNativeTargetTypes.appExtension, project, platformData); this.configureTarget(extensionFolder, path.join(extensionsFolderPath, extensionFolder), target, project, projectData); targetUuids.push(target.uuid); addedExtensions = true; }); this.$fs.writeFile(pbxProjPath, project.writeSync({omitEmptyValues: true})); - this.prepareSigning(targetUuids, projectData, pbxProjPath); + this.$iOSNativeTargetService.prepareSigning(targetUuids, projectData, pbxProjPath); return addedExtensions; } @@ -34,12 +33,12 @@ export class IOSExtensionsService extends NativeTargetServiceBase implements IIO private configureTarget(extensionName: string, extensionPath: string, target: IXcode.target, project: IXcode.project, projectData: IProjectData) { const extJsonPath = path.join(extensionPath, "extension.json"); - this.setXcodeTargetBuildConfigurationProperties( + this.$iOSNativeTargetService.setXcodeTargetBuildConfigurationProperties( [{name: "PRODUCT_BUNDLE_IDENTIFIER", value: `${projectData.projectIdentifiers.ios}.${extensionName}`}], extensionName, project); - this.setConfigurationsFromJsonFile(extJsonPath, target.uuid, extensionName, project); + this.$iOSNativeTargetService.setConfigurationsFromJsonFile(extJsonPath, target.uuid, extensionName, project); } public removeExtensions({pbxProjPath}: IRemoveExtensionsOptions): void { diff --git a/lib/services/ios-native-target-service-base.ts b/lib/services/ios-native-target-service.ts similarity index 59% rename from lib/services/ios-native-target-service-base.ts rename to lib/services/ios-native-target-service.ts index 5f203d405b..b8be774f6e 100644 --- a/lib/services/ios-native-target-service-base.ts +++ b/lib/services/ios-native-target-service.ts @@ -1,39 +1,27 @@ import * as path from "path"; -export enum BuildNames { - debug = "Debug", - release = "Release" -} - -export interface IXcodeTargetBuildConfigurationProperty { - name: string; - value: any; - buildNames?: BuildNames[]; -} - -export abstract class NativeTargetServiceBase { +export class IOSNativeTargetService implements IIOSNativeTargetService { constructor(protected $fs: IFileSystem, - protected $pbxprojDomXcode: IPbxprojDomXcode, - protected $xcode: IXcode) { + protected $pbxprojDomXcode: IPbxprojDomXcode) { } - protected addTargetToProject(extensionsFolderPath: string, extensionFolder: string, targetType: string, project: IXcode.project, platformData: IPlatformData, parentTarget?: string): IXcode.target { - const extensionPath = path.join(extensionsFolderPath, extensionFolder); - const extensionRelativePath = path.relative(platformData.projectRoot, extensionPath); - const files = this.$fs.readDirectory(extensionPath) + public addTargetToProject(targetRootPath: string, targetFolder: string, targetType: string, project: IXcode.project, platformData: IPlatformData, parentTarget?: string): IXcode.target { + const targetPath = path.join(targetRootPath, targetFolder); + const targetRelativePath = path.relative(platformData.projectRoot, targetPath); + const files = this.$fs.readDirectory(targetPath) .filter(filePath => !filePath.startsWith(".")) - .map(filePath => path.join(extensionPath, filePath)); - const target = project.addTarget(extensionFolder, targetType, extensionRelativePath, parentTarget); + .map(filePath => path.join(targetPath, filePath)); + const target = project.addTarget(targetFolder, targetType, targetRelativePath, parentTarget); project.addBuildPhase([], 'PBXSourcesBuildPhase', 'Sources', target.uuid); project.addBuildPhase([], 'PBXResourcesBuildPhase', 'Resources', target.uuid); project.addBuildPhase([], 'PBXFrameworksBuildPhase', 'Frameworks', target.uuid); - project.addPbxGroup(files, extensionFolder, extensionPath, null, { isMain: true, target: target.uuid, filesRelativeToProject: true }); - project.addToHeaderSearchPaths(extensionPath, target.pbxNativeTarget.productName); + project.addPbxGroup(files, targetFolder, targetPath, null, { isMain: true, target: target.uuid, filesRelativeToProject: true }); + project.addToHeaderSearchPaths(targetPath, target.pbxNativeTarget.productName); return target; } - protected prepareSigning(targetUuids: string[], projectData:IProjectData, projectPath: string) { + public prepareSigning(targetUuids: string[], projectData: IProjectData, projectPath: string): void { const xcode = this.$pbxprojDomXcode.Xcode.open(projectPath); const signing = xcode.getSigning(projectData.projectName); if (signing !== undefined) { @@ -52,7 +40,7 @@ export abstract class NativeTargetServiceBase { xcode.save(); } - protected getTargetDirectories(folderPath: string): string[] { + public getTargetDirectories(folderPath: string): string[] { return this.$fs.readDirectory(folderPath) .filter(fileName => { const filePath = path.join(folderPath, fileName); @@ -62,7 +50,7 @@ export abstract class NativeTargetServiceBase { }); } - protected setXcodeTargetBuildConfigurationProperties(properties: IXcodeTargetBuildConfigurationProperty[], targetName: string, project: IXcode.project): void { + public setXcodeTargetBuildConfigurationProperties(properties: IXcodeTargetBuildConfigurationProperty[], targetName: string, project: IXcode.project): void { properties.forEach(property => { const buildNames = property.buildNames || [BuildNames.debug, BuildNames.release]; buildNames.forEach((buildName) => { @@ -71,7 +59,7 @@ export abstract class NativeTargetServiceBase { }); } - protected setConfigurationsFromJsonFile(jsonPath: string, targetUuid: string, targetName: string, project: IXcode.project) { + public setConfigurationsFromJsonFile(jsonPath: string, targetUuid: string, targetName: string, project: IXcode.project): void { if (this.$fs.exists(jsonPath)) { const configurationJson = this.$fs.readJson(jsonPath) || {}; @@ -94,3 +82,5 @@ export abstract class NativeTargetServiceBase { } } } + +$injector.register("iOSNativeTargetService", IOSNativeTargetService); diff --git a/lib/services/ios-project-service.ts b/lib/services/ios-project-service.ts index a7d93a4f5d..784fd90388 100644 --- a/lib/services/ios-project-service.ts +++ b/lib/services/ios-project-service.ts @@ -52,7 +52,8 @@ export class IOSProjectService extends projectServiceBaseLib.PlatformProjectServ private $xcconfigService: IXcconfigService, private $xcodebuildService: IXcodebuildService, private $iOSExtensionsService: IIOSExtensionsService, - private $iOSWatchAppService: IIOSWatchAppService) { + private $iOSWatchAppService: IIOSWatchAppService, + private $iOSNativeTargetService: IIOSNativeTargetService) { super($fs, $projectDataService); } @@ -432,7 +433,7 @@ export class IOSProjectService extends projectServiceBaseLib.PlatformProjectServ CFBundleIdentifier - ${projectData.projectIdentifiers.ios} + $(PRODUCT_BUNDLE_IDENTIFIER) ` }); @@ -529,6 +530,8 @@ export class IOSProjectService extends projectServiceBaseLib.PlatformProjectServ public async handleNativeDependenciesChange(projectData: IProjectData, opts: IRelease): Promise { const platformData = this.getPlatformData(projectData); + + this.setProductBundleIdentifier(projectData); await this.$cocoapodsService.applyPodfileFromAppResources(projectData, platformData); const projectPodfilePath = this.$cocoapodsService.getProjectPodfilePath(platformData.projectRoot); @@ -603,6 +606,17 @@ export class IOSProjectService extends projectServiceBaseLib.PlatformProjectServ return target; } + private setProductBundleIdentifier(projectData: IProjectData): void { + const project = this.createPbxProj(projectData); + this.$iOSNativeTargetService.setXcodeTargetBuildConfigurationProperties([ + { + name: "PRODUCT_BUNDLE_IDENTIFIER", + value: `"${projectData.projectIdentifiers.ios}"` + } + ], projectData.projectName, project); + this.savePbxProj(project, projectData); + } + private getAllLibsForPluginWithFileExtension(pluginData: IPluginData, fileExtension: string | string[]): string[] { const fileExtensions = _.isArray(fileExtension) ? fileExtension : [fileExtension]; const filterCallback = (fileName: string, pluginPlatformsFolderPath: string) => diff --git a/lib/services/ios-watch-app-service.ts b/lib/services/ios-watch-app-service.ts index d5b6886e45..3c4dad7e5c 100644 --- a/lib/services/ios-watch-app-service.ts +++ b/lib/services/ios-watch-app-service.ts @@ -1,14 +1,13 @@ import * as path from "path"; -import { NativeTargetServiceBase } from "./ios-native-target-service-base"; import { IOSDeviceTargets, IOS_WATCHAPP_FOLDER, IOS_WATCHAPP_EXTENSION_FOLDER, IOSNativeTargetProductTypes, IOSNativeTargetTypes } from "../constants"; -export class IOSWatchAppService extends NativeTargetServiceBase implements IIOSWatchAppService { +export class IOSWatchAppService implements IIOSWatchAppService { private static WATCH_APP_IDENTIFIER = "watchkitapp"; private static WACTCH_EXTENSION_IDENTIFIER = "watchkitextension"; constructor(protected $fs: IFileSystem, protected $pbxprojDomXcode: IPbxprojDomXcode, - protected $xcode: IXcode) { - super($fs, $pbxprojDomXcode, $xcode); + protected $xcode: IXcode, + private $iOSNativeTargetService: IIOSNativeTargetService) { } public async addWatchAppFromPath({watchAppFolderPath, projectData, platformData, pbxProjPath}: IAddWatchAppFromPathOptions): Promise { @@ -20,13 +19,13 @@ export class IOSWatchAppService extends NativeTargetServiceBase implements IIOSW return false; } - const appFolder = this.getTargetDirectories(appPath)[0]; - const extensionFolder = this.getTargetDirectories(extensionPath)[0]; + const appFolder = this.$iOSNativeTargetService.getTargetDirectories(appPath)[0]; + const extensionFolder = this.$iOSNativeTargetService.getTargetDirectories(extensionPath)[0]; const project = new this.$xcode.project(pbxProjPath); project.parseSync(); - const watchApptarget = this.addTargetToProject(appPath, appFolder, IOSNativeTargetTypes.watchApp, project, platformData, project.getFirstTarget().uuid); + const watchApptarget = this.$iOSNativeTargetService.addTargetToProject(appPath, appFolder, IOSNativeTargetTypes.watchApp, project, platformData, project.getFirstTarget().uuid); this.configureTarget( appFolder, path.join(appPath, appFolder), @@ -37,7 +36,7 @@ export class IOSWatchAppService extends NativeTargetServiceBase implements IIOSW ); targetUuids.push(watchApptarget.uuid); - const watchExtensionTarget = this.addTargetToProject(extensionPath, extensionFolder, IOSNativeTargetTypes.watchExtension, project, platformData, watchApptarget.uuid); + const watchExtensionTarget = this.$iOSNativeTargetService.addTargetToProject(extensionPath, extensionFolder, IOSNativeTargetTypes.watchExtension, project, platformData, watchApptarget.uuid); this.configureTarget( extensionFolder, path.join(extensionPath, extensionFolder), @@ -48,7 +47,7 @@ export class IOSWatchAppService extends NativeTargetServiceBase implements IIOSW targetUuids.push(watchExtensionTarget.uuid); this.$fs.writeFile(pbxProjPath, project.writeSync({omitEmptyValues: true})); - this.prepareSigning(targetUuids, projectData, pbxProjPath); + this.$iOSNativeTargetService.prepareSigning(targetUuids, projectData, pbxProjPath); return true; } @@ -78,7 +77,7 @@ export class IOSWatchAppService extends NativeTargetServiceBase implements IIOSW identifierParts.pop(); const wkAppBundleIdentifier = identifierParts.join("."); - this.setXcodeTargetBuildConfigurationProperties([ + this.$iOSNativeTargetService.setXcodeTargetBuildConfigurationProperties([ {name: "PRODUCT_BUNDLE_IDENTIFIER", value: identifier}, {name: "SDKROOT", value: "watchos"}, {name: "TARGETED_DEVICE_FAMILY", value: IOSDeviceTargets.watchos}, @@ -86,7 +85,7 @@ export class IOSWatchAppService extends NativeTargetServiceBase implements IIOSW {name: "WK_APP_BUNDLE_IDENTIFIER", value: wkAppBundleIdentifier} ], targetName, project); - this.setConfigurationsFromJsonFile(targetConfigurationJsonPath, target.uuid, targetName, project); + this.$iOSNativeTargetService.setConfigurationsFromJsonFile(targetConfigurationJsonPath, target.uuid, targetName, project); project.addToHeaderSearchPaths(targetPath, target.pbxNativeTarget.productName); } } diff --git a/test/ios-project-service.ts b/test/ios-project-service.ts index f980f15267..fd1b2ed35c 100644 --- a/test/ios-project-service.ts +++ b/test/ios-project-service.ts @@ -174,6 +174,9 @@ function createTestInjector(projectPath: string, projectName: string, xCode?: IX testInjector.register("logSourceMapService", { replaceWithOriginalFileLocations: (platform: string, message: string) => message }); + testInjector.register("iOSNativeTargetService", { + setXcodeTargetBuildConfigurationProperties: () => { /* */ } + }); return testInjector; }