Skip to content

Commit 229769f

Browse files
committed
feat: Add support for a Podfile in App_Resources/iOS
1 parent cf37f1c commit 229769f

File tree

8 files changed

+72
-33
lines changed

8 files changed

+72
-33
lines changed

lib/constants.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ require("colors");
33
export const APP_FOLDER_NAME = "app";
44
export const APP_RESOURCES_FOLDER_NAME = "App_Resources";
55
export const PROJECT_FRAMEWORK_FOLDER_NAME = "framework";
6+
export const NS_BASE_PODFILE = "NSPodfileBase";
67
export const NATIVESCRIPT_KEY_NAME = "nativescript";
78
export const NODE_MODULES_FOLDER_NAME = "node_modules";
89
export const TNS_MODULES_FOLDER_NAME = "tns_modules";

lib/definitions/project.d.ts

Lines changed: 25 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,7 @@ interface IProjectData extends ICreateProjectData {
9292
gradleFilesDirectoryPath: string;
9393
infoPlistPath: string;
9494
buildXcconfigPath: string;
95+
podfilePath: string;
9596
/**
9697
* Defines if the project is a code sharing one.
9798
* Value is true when project has nsconfig.json and it has `shared: true` in it.
@@ -409,7 +410,7 @@ interface IPlatformProjectService extends NodeJS.EventEmitter, IPlatformProjectS
409410
getAppResourcesDestinationDirectoryPath(projectData: IProjectData): string;
410411

411412
cleanDeviceTempFolder(deviceIdentifier: string, projectData: IProjectData): Promise<void>;
412-
processConfigurationFilesFromAppResources(release: boolean, projectData: IProjectData): Promise<void>;
413+
processConfigurationFilesFromAppResources(release: boolean, projectData: IProjectData, installPods: boolean): Promise<void>;
413414

414415
/**
415416
* Ensures there is configuration file (AndroidManifest.xml, Info.plist) in app/App_Resources.
@@ -449,7 +450,7 @@ interface IPlatformProjectService extends NodeJS.EventEmitter, IPlatformProjectS
449450
* Traverse through the production dependencies and find plugins that need build/rebuild
450451
*/
451452
checkIfPluginsNeedBuild(projectData: IProjectData): Promise<Array<any>>;
452-
453+
453454
/**
454455
* Get the deployment target's version
455456
* Currently implemented only for iOS -> returns the value of IPHONEOS_DEPLOYMENT_TARGET property from xcconfig file
@@ -482,23 +483,40 @@ interface ICocoaPodsService {
482483
*/
483484
getPodfileFooter(): string;
484485

486+
/**
487+
* Copies the Podfile(if any) in App_Resources to the project's folder.
488+
* @param {string} mainPodfilePath Path to the main Podfile.
489+
* @param {string} nativeProjectPath Path to the native Xcode project.
490+
* @returns {Promise<void>}
491+
*/
492+
copyMainPodfileToProject(mainPodfilePath: string, nativeProjectPath: string): Promise<void>;
493+
485494
/**
486495
* Prepares the Podfile content of a plugin and merges it in the project's Podfile.
487-
* @param {IPluginData} pluginData Information about the plugin.
496+
* @param {string} moduleName The module which the Podfile is from.
497+
* @param {string} podfilePath The path to the podfile.
488498
* @param {IProjectData} projectData Information about the project.
489499
* @param {string} nativeProjectPath Path to the native Xcode project.
490500
* @returns {Promise<void>}
491501
*/
492-
applyPluginPodfileToProject(pluginData: IPluginData, projectData: IProjectData, nativeProjectPath: string): Promise<void>;
502+
applyPodfileToProject(moduleName: string, podfilePath: string, projectData: IProjectData, nativeProjectPath: string): Promise<void>;
493503

494504
/**
495-
* Removes plugins Podfile content from the project.
505+
* Gives the path to the plugin's Podfile.
496506
* @param {IPluginData} pluginData Information about the plugin.
497-
* @param {IProjectData} projectData Information about the project.
507+
* @returns {string} Path to plugin's Podfile
508+
*/
509+
getPluginPodfilePath(pluginData: IPluginData): string;
510+
511+
/**
512+
* Removes plugins Podfile content from the project.
513+
* @param {string} moduleName The name of the module.
514+
* @param {string} podfilePath The path to the module's Podfile.
515+
* @param {string} projectData Information about the project.
498516
* @param {string} nativeProjectPath Path to the native Xcode project.
499517
* @returns {void}
500518
*/
501-
removePluginPodfileFromProject(pluginData: IPluginData, projectData: IProjectData, nativeProjectPath: string): void;
519+
removePodfileFromProject(moduleName: string, podfilePath: string, projectData: IProjectData, nativeProjectPath: string): void;
502520

503521
/**
504522
* Gives the path to project's Podfile.

lib/project-data.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,7 @@ export class ProjectData implements IProjectData {
5959
public appGradlePath: string;
6060
public gradleFilesDirectoryPath: string;
6161
public buildXcconfigPath: string;
62+
public podfilePath: string;
6263
public isShared: boolean;
6364

6465
constructor(private $fs: IFileSystem,
@@ -132,6 +133,7 @@ export class ProjectData implements IProjectData {
132133
this.appGradlePath = path.join(this.gradleFilesDirectoryPath, constants.APP_GRADLE_FILE_NAME);
133134
this.infoPlistPath = path.join(this.appResourcesDirectoryPath, this.$devicePlatformsConstants.iOS, constants.INFO_PLIST_FILE_NAME);
134135
this.buildXcconfigPath = path.join(this.appResourcesDirectoryPath, this.$devicePlatformsConstants.iOS, constants.BUILD_XCCONFIG_FILE_NAME);
136+
this.podfilePath = path.join(this.appResourcesDirectoryPath, this.$devicePlatformsConstants.iOS, constants.PODFILE_NAME);
135137
this.isShared = !!(this.nsConfig && this.nsConfig.shared);
136138
return;
137139
}

lib/services/cocoapods-service.ts

Lines changed: 20 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -52,39 +52,40 @@ export class CocoaPodsService implements ICocoaPodsService {
5252
return podInstallResult;
5353
}
5454

55-
public async applyPluginPodfileToProject(pluginData: IPluginData, projectData: IProjectData, nativeProjectPath: string): Promise<void> {
56-
const pluginPodFilePath = this.getPluginPodfilePath(pluginData);
57-
if (!this.$fs.exists(pluginPodFilePath)) {
55+
public async applyPodfileToProject(moduleName: string, podfilePath: string, projectData: IProjectData, nativeProjectPath: string): Promise<void> {
56+
if (!this.$fs.exists(podfilePath)) {
57+
if (podfilePath === projectData.podfilePath) {
58+
this.removePodfileFromProject(moduleName, podfilePath, projectData, nativeProjectPath);
59+
}
5860
return;
5961
}
6062

61-
const { pluginPodfileContent, replacedFunctions } = this.buildPodfileContent(pluginPodFilePath, pluginData.name);
63+
const { podfileContent, replacedFunctions } = this.buildPodfileContent(podfilePath, moduleName);
6264
const pathToProjectPodfile = this.getProjectPodfilePath(nativeProjectPath);
6365
const projectPodfileContent = this.$fs.exists(pathToProjectPodfile) ? this.$fs.readText(pathToProjectPodfile).trim() : "";
6466

65-
if (projectPodfileContent.indexOf(pluginPodfileContent) === -1) {
67+
if (projectPodfileContent.indexOf(podfileContent) === -1) {
6668
// Remove old occurences of the plugin from the project's Podfile.
67-
this.removePluginPodfileFromProject(pluginData, projectData, nativeProjectPath);
69+
this.removePodfileFromProject(moduleName, podfilePath, projectData, nativeProjectPath);
6870
let finalPodfileContent = this.$fs.exists(pathToProjectPodfile) ? this.getPodfileContentWithoutTarget(projectData, this.$fs.readText(pathToProjectPodfile)) : "";
6971

70-
if (pluginPodfileContent.indexOf(CocoaPodsService.PODFILE_POST_INSTALL_SECTION_NAME) !== -1) {
71-
finalPodfileContent = this.addPostInstallHook(replacedFunctions, finalPodfileContent, pluginPodfileContent);
72+
if (podfileContent.indexOf(CocoaPodsService.PODFILE_POST_INSTALL_SECTION_NAME) !== -1) {
73+
finalPodfileContent = this.addPostInstallHook(replacedFunctions, finalPodfileContent, podfileContent);
7274
}
7375

74-
finalPodfileContent = `${pluginPodfileContent}${EOL}${finalPodfileContent}`;
76+
finalPodfileContent = `${podfileContent}${EOL}${finalPodfileContent}`;
7577
this.saveProjectPodfile(projectData, finalPodfileContent, nativeProjectPath);
7678
}
7779
}
7880

79-
public removePluginPodfileFromProject(pluginData: IPluginData, projectData: IProjectData, projectRoot: string): void {
80-
const pluginPodfilePath = this.getPluginPodfilePath(pluginData);
81+
public removePodfileFromProject(moduleName: string, podfilePath: string, projectData: IProjectData, projectRoot: string): void {
8182

82-
if (this.$fs.exists(pluginPodfilePath) && this.$fs.exists(this.getProjectPodfilePath(projectRoot))) {
83+
if ((this.$fs.exists(podfilePath) || podfilePath == projectData.podfilePath) && this.$fs.exists(this.getProjectPodfilePath(projectRoot))) {
8384
let projectPodFileContent = this.$fs.readText(this.getProjectPodfilePath(projectRoot));
8485
// Remove the data between #Begin Podfile and #EndPodfile
85-
const regExpToRemove = new RegExp(`${this.getPluginPodfileHeader(pluginPodfilePath)}[\\s\\S]*?${this.getPluginPodfileEnd()}`, "mg");
86+
const regExpToRemove = new RegExp(`${this.getPluginPodfileHeader(podfilePath)}[\\s\\S]*?${this.getPluginPodfileEnd()}`, "mg");
8687
projectPodFileContent = projectPodFileContent.replace(regExpToRemove, "");
87-
projectPodFileContent = this.removePostInstallHook(pluginData, projectPodFileContent);
88+
projectPodFileContent = this.removePostInstallHook(moduleName, projectPodFileContent);
8889

8990
const defaultPodfileBeginning = this.getPodfileHeader(projectData.projectName);
9091
const defaultContentWithPostInstallHook = `${defaultPodfileBeginning}${EOL}${this.getPostInstallHookHeader()}end${EOL}end`;
@@ -98,7 +99,7 @@ export class CocoaPodsService implements ICocoaPodsService {
9899
}
99100
}
100101

101-
private getPluginPodfilePath(pluginData: IPluginData): string {
102+
public getPluginPodfilePath(pluginData: IPluginData): string {
102103
const pluginPlatformsFolderPath = pluginData.pluginPlatformsFolderPath(PluginNativeDirNames.iOS);
103104
const pluginPodFilePath = path.join(pluginPlatformsFolderPath, PODFILE_NAME);
104105
return pluginPodFilePath;
@@ -157,8 +158,8 @@ export class CocoaPodsService implements ICocoaPodsService {
157158
this.$fs.writeFile(projectPodfilePath, contentToWrite);
158159
}
159160

160-
private removePostInstallHook(pluginData: IPluginData, projectPodFileContent: string): string {
161-
const regExp = new RegExp(`^.*?${this.getHookBasicFuncNameForPlugin(CocoaPodsService.PODFILE_POST_INSTALL_SECTION_NAME, pluginData.name)}.*?$\\r?\\n`, "gm");
161+
private removePostInstallHook(moduleName: string, projectPodFileContent: string): string {
162+
const regExp = new RegExp(`^.*?${this.getHookBasicFuncNameForPlugin(CocoaPodsService.PODFILE_POST_INSTALL_SECTION_NAME, moduleName)}.*?$\\r?\\n`, "gm");
162163
projectPodFileContent = projectPodFileContent.replace(regExp, "");
163164
return projectPodFileContent;
164165
}
@@ -206,12 +207,12 @@ export class CocoaPodsService implements ICocoaPodsService {
206207
return `${CocoaPodsService.PODFILE_POST_INSTALL_SECTION_NAME} do |${CocoaPodsService.INSTALLER_BLOCK_PARAMETER_NAME}|${EOL}`;
207208
}
208209

209-
private buildPodfileContent(pluginPodFilePath: string, pluginName: string): { pluginPodfileContent: string, replacedFunctions: IRubyFunction[] } {
210+
private buildPodfileContent(pluginPodFilePath: string, pluginName: string): { podfileContent: string, replacedFunctions: IRubyFunction[] } {
210211
const pluginPodfileContent = this.$fs.readText(pluginPodFilePath);
211212
const { replacedContent, newFunctions: replacedFunctions } = this.replaceHookContent(CocoaPodsService.PODFILE_POST_INSTALL_SECTION_NAME, pluginPodfileContent, pluginName);
212213

213214
return {
214-
pluginPodfileContent: `${this.getPluginPodfileHeader(pluginPodFilePath)}${EOL}${replacedContent}${EOL}${this.getPluginPodfileEnd()}`,
215+
podfileContent: `${this.getPluginPodfileHeader(pluginPodFilePath)}${EOL}${replacedContent}${EOL}${this.getPluginPodfileEnd()}`,
215216
replacedFunctions
216217
};
217218
}

lib/services/ios-project-service.ts

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import { IOSEntitlementsService } from "./ios-entitlements-service";
1414
import { XCConfigService } from "./xcconfig-service";
1515
import * as mobileprovision from "ios-mobileprovision-finder";
1616
import { BUILD_XCCONFIG_FILE_NAME, IosProjectConstants } from "../constants";
17+
import { project } from "xcode";
1718

1819
interface INativeSourceCodeGroup {
1920
name: string;
@@ -782,14 +783,15 @@ We will now place an empty obsolete compatability white screen LauncScreen.xib f
782783
const filterFile = (filename: string) => this.$fs.deleteFile(path.join(platformFolder, filename));
783784

784785
filterFile(this.getPlatformData(projectData).configurationFileName);
786+
filterFile(constants.PODFILE_NAME);
785787

786788
// src folder should not be copied as the pbxproject will have references to its files
787789
this.$fs.deleteDirectory(path.join(appResourcesDirectoryPath, this.getPlatformData(projectData).normalizedPlatformName, "src"));
788790

789791
this.$fs.deleteDirectory(this.getAppResourcesDestinationDirectoryPath(projectData));
790792
}
791793

792-
public async processConfigurationFilesFromAppResources(release: boolean, projectData: IProjectData): Promise<void> {
794+
public async processConfigurationFilesFromAppResources(release: boolean, projectData: IProjectData, installPods: boolean): Promise<void> {
793795
await this.mergeInfoPlists({ release }, projectData);
794796
await this.$iOSEntitlementsService.merge(projectData);
795797
await this.mergeProjectXcconfigFiles(release, projectData);
@@ -798,6 +800,10 @@ We will now place an empty obsolete compatability white screen LauncScreen.xib f
798800
}
799801

800802
this.$pluginVariablesService.interpolateAppIdentifier(this.getPlatformData(projectData).configurationFilePath, projectData.projectIdentifiers.ios);
803+
804+
if (installPods) {
805+
await this.installPodsIfAny(projectData);
806+
}
801807
}
802808

803809
private getInfoPlistPath(projectData: IProjectData): string {
@@ -960,7 +966,7 @@ We will now place an empty obsolete compatability white screen LauncScreen.xib f
960966
await this.prepareStaticLibs(pluginPlatformsFolderPath, pluginData, projectData);
961967

962968
const projectRoot = this.getPlatformData(projectData).projectRoot;
963-
await this.$cocoapodsService.applyPluginPodfileToProject(pluginData, projectData, projectRoot);
969+
await this.$cocoapodsService.applyPodfileToProject(pluginData.name, this.$cocoapodsService.getPluginPodfilePath(pluginData), projectData, projectRoot);
964970
}
965971

966972
public async removePluginNativeCode(pluginData: IPluginData, projectData: IProjectData): Promise<void> {
@@ -971,10 +977,14 @@ We will now place an empty obsolete compatability white screen LauncScreen.xib f
971977
this.removeStaticLibs(pluginPlatformsFolderPath, pluginData, projectData);
972978
const projectRoot = this.getPlatformData(projectData).projectRoot;
973979

974-
this.$cocoapodsService.removePluginPodfileFromProject(pluginData, projectData, projectRoot);
980+
this.$cocoapodsService.removePodfileFromProject(pluginData.name, this.$cocoapodsService.getPluginPodfilePath(pluginData), projectData, projectRoot);
975981
}
976982

977983
public async afterPrepareAllPlugins(projectData: IProjectData): Promise<void> {
984+
await this.installPodsIfAny(projectData);
985+
}
986+
987+
public async installPodsIfAny(projectData: IProjectData): Promise<void> {
978988
const projectRoot = this.getPlatformData(projectData).projectRoot;
979989
if (this.$fs.exists(this.$cocoapodsService.getProjectPodfilePath(projectRoot))) {
980990
const xcodeProjPath = this.getXcodeprojPath(projectData);
@@ -989,6 +999,9 @@ We will now place an empty obsolete compatability white screen LauncScreen.xib f
989999
await this.$childProcess.exec(createSchemeRubyScript, { cwd: this.getPlatformData(projectData).projectRoot });
9901000
}
9911001

1002+
const mainPodfilePath = path.join(projectData.appResourcesDirectoryPath, this.getPlatformData(projectData).normalizedPlatformName, constants.PODFILE_NAME);
1003+
await this.$cocoapodsService.applyPodfileToProject(constants.NS_BASE_PODFILE, mainPodfilePath, projectData, this.getPlatformData(projectData).projectRoot);
1004+
9921005
await this.$cocoapodsService.executePodInstall(projectRoot, xcodeProjPath);
9931006
}
9941007
}

lib/services/prepare-platform-native-service.ts

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,9 @@ export class PreparePlatformNativeService extends PreparePlatformService impleme
3939
await config.platformData.platformProjectService.prepareProject(config.projectData, config.platformSpecificData);
4040
}
4141

42-
if (!config.changesInfo || config.changesInfo.modulesChanged) {
42+
const shouldPrepareModules = !config.changesInfo || config.changesInfo.modulesChanged;
43+
44+
if (shouldPrepareModules) {
4345
await this.$pluginsService.validate(config.platformData, config.projectData);
4446

4547
const appDestinationDirectoryPath = path.join(config.platformData.appDestinationDirectoryPath, constants.APP_FOLDER_NAME);
@@ -60,7 +62,8 @@ export class PreparePlatformNativeService extends PreparePlatformService impleme
6062
}
6163

6264
if (!config.changesInfo || config.changesInfo.configChanged || config.changesInfo.modulesChanged) {
63-
await config.platformData.platformProjectService.processConfigurationFilesFromAppResources(config.appFilesUpdaterOptions.release, config.projectData);
65+
// Passing !shouldPrepareModules` we assume that if the node modules are prepared base Podfile content is added and `pod install` is executed.
66+
await config.platformData.platformProjectService.processConfigurationFilesFromAppResources(config.appFilesUpdaterOptions.release, config.projectData, !shouldPrepareModules);
6467
}
6568

6669
config.platformData.platformProjectService.interpolateConfigurationFile(config.projectData, config.platformSpecificData);

test/cocoapods-service.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -511,7 +511,7 @@ end`,
511511
it(testCase.testCaseDescription, async () => {
512512
mockFileSystem(testInjector, testCase.input, testCase.projectPodfileContent);
513513

514-
await cocoapodsService.applyPluginPodfileToProject(testCase.pluginData || mockPluginData, mockProjectData, nativeProjectPath);
514+
await cocoapodsService.applyPodfileToProject(testCase.pluginData ? testCase.pluginData.name : mockPluginData.name, cocoapodsService.getPluginPodfilePath(testCase.pluginData || mockPluginData), mockProjectData, nativeProjectPath);
515515

516516
assert.deepEqual(changeNewLineCharacter(newPodfileContent), changeNewLineCharacter(testCase.output));
517517
});
@@ -720,7 +720,7 @@ end`
720720
it(testCase.testCaseDescription, async () => {
721721
mockFileSystem(testInjector, testCase.input, testCase.projectPodfileContent);
722722

723-
cocoapodsService.removePluginPodfileFromProject(mockPluginData, mockProjectData, nativeProjectPath);
723+
cocoapodsService.removePodfileFromProject(mockPluginData.name, cocoapodsService.getPluginPodfilePath(mockPluginData), mockProjectData, nativeProjectPath);
724724

725725
assert.deepEqual(changeNewLineCharacter(newPodfileContent), changeNewLineCharacter(testCase.output));
726726
});

test/stubs.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -323,6 +323,7 @@ export class ProjectDataStub implements IProjectData {
323323
public appGradlePath: string;
324324
public gradleFilesDirectoryPath: string;
325325
public buildXcconfigPath: string;
326+
public podfilePath: string;
326327
public isShared: boolean;
327328

328329
public initializeProjectData(projectDir?: string): void {

0 commit comments

Comments
 (0)