Skip to content

Commit 6caa6f6

Browse files
committed
refactor: extract extensions service
1 parent 860893f commit 6caa6f6

File tree

6 files changed

+124
-90
lines changed

6 files changed

+124
-90
lines changed

lib/bootstrap.ts

+1
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ $injector.requirePublic("projectService", "./services/project-service");
1212
$injector.require("androidProjectService", "./services/android-project-service");
1313
$injector.require("androidPluginBuildService", "./services/android-plugin-build-service");
1414
$injector.require("iOSEntitlementsService", "./services/ios-entitlements-service");
15+
$injector.require("iOSExtensionsService", "./services/ios-extensions-service");
1516
$injector.require("iOSProjectService", "./services/ios-project-service");
1617
$injector.require("iOSProvisionService", "./services/ios-provision-service");
1718
$injector.require("xcconfigService", "./services/xcconfig-service");

lib/definitions/project.d.ts

+14
Original file line numberDiff line numberDiff line change
@@ -472,6 +472,12 @@ interface IPlatformProjectService extends NodeJS.EventEmitter, IPlatformProjectS
472472
getDeploymentTarget(projectData: IProjectData): any;
473473
}
474474

475+
interface INativeSourceCodeGroup {
476+
name: string;
477+
path: string;
478+
files: string[];
479+
}
480+
475481
interface IValidatePlatformOutput {
476482
checkEnvironmentRequirementsOutput: ICheckEnvironmentRequirementsOutput;
477483
}
@@ -566,6 +572,14 @@ interface ICocoaPodsPlatformManager {
566572
replacePlatformRow(podfileContent: string, podfilePath: string): { replacedContent: string, podfilePlatformData: IPodfilePlatformData };
567573
}
568574

575+
/**
576+
* Describes a service used to add and remove iOS extension
577+
*/
578+
interface IIOSExtensionsService {
579+
addExtensionsFromPath(extensionsFolderPath: string, projectData: IProjectData, platformData: IPlatformData, projectPath: string, project: IXcode.project): Promise<void>;
580+
removeExtensions(project: IXcode.project, projectPath: string): void;
581+
}
582+
569583
interface IRubyFunction {
570584
functionName: string;
571585
functionParameters?: string;

lib/definitions/xcode.d.ts

+2-1
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ declare module "nativescript-dev-xcode" {
1313
parse(callback: () => void): void;
1414
parseSync(): void;
1515

16-
writeSync(): string;
16+
writeSync(options: any): string;
1717

1818
addFramework(filepath: string, options?: Options): void;
1919
removeFramework(filePath: string, options?: Options): void;
@@ -46,6 +46,7 @@ declare module "nativescript-dev-xcode" {
4646
): group;
4747
addBuildProperty(prop: string, value: any, build_name?: string, productName?: string): void;
4848
addToHeaderSearchPaths(file: string|Object, productName?: string): void;
49+
removeTargetsByProductType(targetType: string): void
4950
}
5051

5152
class target {
+89
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
import * as path from "path";
2+
3+
export class IOSExtensionsService implements IIOSExtensionsService {
4+
constructor(private $fs: IFileSystem,
5+
private $pbxprojDomXcode: IPbxprojDomXcode) {
6+
}
7+
8+
public async addExtensionsFromPath(extensionsFolderPath: string, projectData: IProjectData, platformData: IPlatformData, projectPath: string, project: IXcode.project): Promise<void> {
9+
const targetUuids: string[] = [];
10+
if (!this.$fs.exists(extensionsFolderPath)) {
11+
return;
12+
}
13+
14+
this.$fs.readDirectory(extensionsFolderPath)
15+
.filter(fileName => {
16+
const filePath = path.join(extensionsFolderPath, fileName);
17+
const stats = this.$fs.getFsStats(filePath);
18+
19+
return stats.isDirectory() && !fileName.startsWith(".");
20+
})
21+
.forEach(extensionFolder => {
22+
const targetUuid = this.addExtensionToProject(extensionsFolderPath, extensionFolder, project, projectData, platformData);
23+
targetUuids.push(targetUuid);
24+
});
25+
26+
this.$fs.writeFile(projectPath, project.writeSync({omitEmptyValues: true}));
27+
this.prepareExtensionSigning(targetUuids, projectData, projectPath);
28+
}
29+
30+
private addExtensionToProject(extensionsFolderPath: string, extensionFolder: string, project: IXcode.project, projectData: IProjectData, platformData: IPlatformData): string {
31+
const extensionPath = path.join(extensionsFolderPath, extensionFolder);
32+
const extensionRelativePath = path.relative(platformData.projectRoot, extensionPath);
33+
const files = this.$fs.readDirectory(extensionPath)
34+
.filter(filePath => !filePath.startsWith("."))
35+
.map(filePath => path.join(extensionPath, filePath));
36+
const group: INativeSourceCodeGroup = { name: extensionFolder, path: extensionPath, files};
37+
const target = project.addTarget(extensionFolder, 'app_extension', extensionRelativePath);
38+
project.addBuildPhase([], 'PBXSourcesBuildPhase', 'Sources', target.uuid);
39+
project.addBuildPhase([], 'PBXResourcesBuildPhase', 'Resources', target.uuid);
40+
project.addBuildPhase([], 'PBXFrameworksBuildPhase', 'Frameworks', target.uuid);
41+
42+
const extJsonPath = path.join(extensionsFolderPath, extensionFolder, "extension.json");
43+
if (this.$fs.exists(extJsonPath)) {
44+
const extensionJson = this.$fs.readJson(extJsonPath);
45+
_.forEach(extensionJson.frameworks, framework => {
46+
project.addFramework(
47+
framework,
48+
{ target: target.uuid }
49+
);
50+
});
51+
if (extensionJson.assetcatalogCompilerAppiconName) {
52+
project.addToBuildSettings("ASSETCATALOG_COMPILER_APPICON_NAME", extensionJson.assetcatalogCompilerAppiconName, target.uuid);
53+
}
54+
}
55+
56+
project.addPbxGroup(group.files, group.name, group.path, null, { isMain: true, target: target.uuid, filesRelativeToProject: true });
57+
project.addBuildProperty("PRODUCT_BUNDLE_IDENTIFIER", `${projectData.projectIdentifiers.ios}.${extensionFolder}`, "Debug", extensionFolder);
58+
project.addBuildProperty("PRODUCT_BUNDLE_IDENTIFIER", `${projectData.projectIdentifiers.ios}.${extensionFolder}`, "Release", extensionFolder);
59+
project.addToHeaderSearchPaths(group.path, target.pbxNativeTarget.productName);
60+
61+
return target.uuid;
62+
}
63+
64+
private prepareExtensionSigning(targetUuids: string[], projectData:IProjectData, projectPath: string) {
65+
const xcode = this.$pbxprojDomXcode.Xcode.open(projectPath);
66+
const signing = xcode.getSigning(projectData.projectName);
67+
if (signing !== undefined) {
68+
_.forEach(targetUuids, targetUuid => {
69+
if (signing.style === "Automatic") {
70+
xcode.setAutomaticSigningStyleByTargetKey(targetUuid, signing.team);
71+
} else {
72+
for (const config in signing.configurations) {
73+
const signingConfiguration = signing.configurations[config];
74+
xcode.setManualSigningStyleByTargetKey(targetUuid, signingConfiguration);
75+
break;
76+
}
77+
}
78+
});
79+
}
80+
xcode.save();
81+
}
82+
83+
public removeExtensions(project: IXcode.project, projectPath: string): void {
84+
project.removeTargetsByProductType("com.apple.product-type.app-extension");
85+
this.$fs.writeFile(projectPath, project.writeSync({omitEmptyValues: true}));
86+
}
87+
}
88+
89+
$injector.register("iOSExtensionsService", IOSExtensionsService);

lib/services/ios-project-service.ts

+14-89
Original file line numberDiff line numberDiff line change
@@ -15,12 +15,6 @@ import { IOSEntitlementsService } from "./ios-entitlements-service";
1515
import * as mobileprovision from "ios-mobileprovision-finder";
1616
import { BUILD_XCCONFIG_FILE_NAME, IosProjectConstants } from "../constants";
1717

18-
interface INativeSourceCodeGroup {
19-
name: string;
20-
path: string;
21-
files: string[];
22-
}
23-
2418
const DevicePlatformSdkName = "iphoneos";
2519
const SimulatorPlatformSdkName = "iphonesimulator";
2620

@@ -52,7 +46,8 @@ export class IOSProjectService extends projectServiceBaseLib.PlatformProjectServ
5246
private $platformEnvironmentRequirements: IPlatformEnvironmentRequirements,
5347
private $plistParser: IPlistParser,
5448
private $sysInfo: ISysInfo,
55-
private $xcconfigService: IXcconfigService) {
49+
private $xcconfigService: IXcconfigService,
50+
private $iOSExtensionsService: IIOSExtensionsService) {
5651
super($fs, $projectDataService);
5752
}
5853

@@ -982,7 +977,9 @@ We will now place an empty obsolete compatability white screen LauncScreen.xib f
982977
await this.$cocoapodsService.mergePodXcconfigFile(projectData, platformData, opts);
983978
}
984979

985-
this.removeExtensions(projectData);
980+
const pbxprojPath = this.getPbxProjPath(projectData);
981+
const project = this.createPbxProj(projectData);
982+
this.$iOSExtensionsService.removeExtensions(project, pbxprojPath);
986983
await this.addExtensions(projectData);
987984
}
988985
public beforePrepareAllPlugins(): Promise<void> {
@@ -1100,90 +1097,24 @@ We will now place an empty obsolete compatability white screen LauncScreen.xib f
11001097
}
11011098

11021099
private async addExtensions(projectData: IProjectData): Promise<void> {
1103-
await this.prepareExtensionsCode(path.join(projectData.getAppResourcesDirectoryPath(), this.getPlatformData(projectData).normalizedPlatformName, constants.NATIVE_EXTENSION_FOLDER), projectData);
1100+
const resorcesExtensionsPath = path.join(
1101+
projectData.getAppResourcesDirectoryPath(),
1102+
this.getPlatformData(projectData).normalizedPlatformName, constants.NATIVE_EXTENSION_FOLDER
1103+
);
1104+
const platformData = this.getPlatformData(projectData);
1105+
const pbxProjectPath = this.getPbxProjPath(projectData);
1106+
const project = this.createPbxProj(projectData);
1107+
await this.$iOSExtensionsService.addExtensionsFromPath(resorcesExtensionsPath, projectData, platformData, pbxProjectPath, project);
11041108
const plugins = await this.getAllInstalledPlugins(projectData);
11051109
for (const pluginIndex in plugins) {
11061110
const pluginData = plugins[pluginIndex];
11071111
const pluginPlatformsFolderPath = pluginData.pluginPlatformsFolderPath(IOSProjectService.IOS_PLATFORM_NAME);
11081112

11091113
const extensionPath = path.join(pluginPlatformsFolderPath, constants.NATIVE_EXTENSION_FOLDER);
1110-
await this.prepareExtensionsCode(extensionPath, projectData);
1114+
await this.$iOSExtensionsService.addExtensionsFromPath(extensionPath, projectData, platformData, pbxProjectPath, project);
11111115
}
11121116
}
11131117

1114-
private async prepareExtensionsCode(extensionsFolderPath: string, projectData: IProjectData): Promise<void> {
1115-
const targetUuids: string[] = [];
1116-
if (!this.$fs.exists(extensionsFolderPath)) {
1117-
return;
1118-
}
1119-
1120-
const project = this.createPbxProj(projectData);
1121-
1122-
this.$fs.readDirectory(extensionsFolderPath)
1123-
.filter(fileName => {
1124-
const filePath = path.join(extensionsFolderPath, fileName);
1125-
const stats = this.$fs.getFsStats(filePath);
1126-
return stats.isDirectory() && !fileName.startsWith(".");
1127-
})
1128-
.forEach(extensionFolder => {
1129-
const targetUuid = this.addExtensionToProject(extensionsFolderPath, extensionFolder, project, projectData);
1130-
targetUuids.push(targetUuid);
1131-
});
1132-
1133-
this.savePbxProj(project, projectData, true);
1134-
this.prepareExtensionSigning(targetUuids, projectData);
1135-
}
1136-
1137-
private addExtensionToProject(extensionsFolderPath: string, extensionFolder: string, project: IXcode.project, projectData: IProjectData): string {
1138-
const extensionPath = path.join(extensionsFolderPath, extensionFolder);
1139-
const extensionRelativePath = path.relative(this.getPlatformData(projectData).projectRoot, extensionPath);
1140-
const group = this.getRootGroup(extensionFolder, extensionPath);
1141-
const target = project.addTarget(extensionFolder, 'app_extension', extensionRelativePath);
1142-
project.addBuildPhase([], 'PBXSourcesBuildPhase', 'Sources', target.uuid);
1143-
project.addBuildPhase([], 'PBXResourcesBuildPhase', 'Resources', target.uuid);
1144-
project.addBuildPhase([], 'PBXFrameworksBuildPhase', 'Frameworks', target.uuid);
1145-
1146-
const extJsonPath = path.join(extensionsFolderPath, extensionFolder, "extension.json");
1147-
if (this.$fs.exists(extJsonPath)) {
1148-
const extensionJson = this.$fs.readJson(extJsonPath);
1149-
_.forEach(extensionJson.frameworks, framework => {
1150-
project.addFramework(
1151-
framework,
1152-
{ target: target.uuid }
1153-
);
1154-
});
1155-
if (extensionJson.assetcatalogCompilerAppiconName) {
1156-
project.addToBuildSettings("ASSETCATALOG_COMPILER_APPICON_NAME", extensionJson.assetcatalogCompilerAppiconName, target.uuid);
1157-
}
1158-
}
1159-
1160-
project.addPbxGroup(group.files, group.name, group.path, null, { isMain: true, target: target.uuid, filesRelativeToProject: true });
1161-
project.addBuildProperty("PRODUCT_BUNDLE_IDENTIFIER", `${projectData.projectIdentifiers.ios}.${extensionFolder}`, "Debug", extensionFolder);
1162-
project.addBuildProperty("PRODUCT_BUNDLE_IDENTIFIER", `${projectData.projectIdentifiers.ios}.${extensionFolder}`, "Release", extensionFolder);
1163-
project.addToHeaderSearchPaths(group.path, target.pbxNativeTarget.productName);
1164-
1165-
return target.uuid;
1166-
}
1167-
1168-
private prepareExtensionSigning(targetUuids: string[], projectData:IProjectData) {
1169-
const xcode = this.$pbxprojDomXcode.Xcode.open(this.getPbxProjPath(projectData));
1170-
const signing = xcode.getSigning(projectData.projectName);
1171-
if (signing !== undefined) {
1172-
_.forEach(targetUuids, targetUuid => {
1173-
if (signing.style === "Automatic") {
1174-
xcode.setAutomaticSigningStyleByTargetKey(targetUuid, signing.team);
1175-
} else {
1176-
for (const config in signing.configurations) {
1177-
const signingConfiguration = signing.configurations[config];
1178-
xcode.setManualSigningStyleByTargetKey(targetUuid, signingConfiguration);
1179-
break;
1180-
}
1181-
}
1182-
});
1183-
}
1184-
xcode.save();
1185-
}
1186-
11871118
private getRootGroup(name: string, rootPath: string) {
11881119
const filePathsArr: string[] = [];
11891120
const rootGroup: INativeSourceCodeGroup = { name: name, files: filePathsArr, path: rootPath };
@@ -1233,12 +1164,6 @@ We will now place an empty obsolete compatability white screen LauncScreen.xib f
12331164
this.savePbxProj(project, projectData);
12341165
}
12351166

1236-
private removeExtensions(projectData: IProjectData): void {
1237-
const project = this.createPbxProj(projectData);
1238-
project.removeTargetsByProductType("com.apple.product-type.app-extension");
1239-
this.savePbxProj(project, projectData);
1240-
}
1241-
12421167
private removeFrameworks(pluginPlatformsFolderPath: string, pluginData: IPluginData, projectData: IProjectData): void {
12431168
const project = this.createPbxProj(projectData);
12441169
_.each(this.getAllLibsForPluginWithFileExtension(pluginData, ".framework"), fileName => {

test/ios-project-service.ts

+4
Original file line numberDiff line numberDiff line change
@@ -156,6 +156,10 @@ function createTestInjector(projectPath: string, projectName: string, xcode?: IX
156156
testInjector.register("pacoteService", {
157157
extractPackage: async (packageName: string, destinationDirectory: string, options?: IPacoteExtractOptions): Promise<void> => undefined
158158
});
159+
testInjector.register("iOSExtensionsService", {
160+
removeExtensions: () => { /* */ },
161+
addExtensionsFromPath: () => Promise.resolve()
162+
});
159163
return testInjector;
160164
}
161165

0 commit comments

Comments
 (0)