Make `react-native link` play nicely with CocoaPods-based iOS projects.

Summary:
The core React Native codebase already has full support for CocoaPods. However, `react-native link` doesn’t play nicely with CocoaPods, so installing third-party libs from the RN ecosystem is really hard.

This change will allow to link projects that contains its own `.podspec` file to CocoaPods-based projects. In case `link` detect `Podfile` in `iOS` directory, it will look for related `.podspec` file in linked project directory, and add it to `Podfile`. If `Podfile` and `.podspec` files are not present, it will fall back to previous implementation.

**Test Plan**
1. Build a React Native project where the iOS part uses CocoaPods to manage its dependencies. The most common scenario here is to have React Native be a Pod dependency, among others.
2. Install a RN-related library, that contains `.podspec` file, with `react-native link` (as an example it could be: [react-native-maps](https://github.com/airbnb/react-native-maps)
3. Building the resulting iOS workspace should succeed (and there should be new entry in `Podfile`)
Closes https://github.com/facebook/react-native/pull/15460

Differential Revision: D6078649

Pulled By: hramos

fbshipit-source-id: 9651085875892fd66299563ca0e42fb2bcc00825
This commit is contained in:
Miron Pawlik 2017-10-17 21:19:53 -07:00 committed by Facebook Github Bot
parent ce937e5b3f
commit 74146cb315
29 changed files with 547 additions and 13 deletions

View File

@ -35,8 +35,8 @@ Install a library with native dependencies:
$ npm install <library-with-native-dependencies> --save
```
**Note:** _`--save` or `--save-dev` flag is very important for this step. React Native will link
your libs based on `dependencies` and `devDependencies` in your `package.json` file._
> ***Note:*** `--save` or `--save-dev` flag is very important for this step. React Native will link
your libs based on `dependencies` and `devDependencies` in your `package.json` file.
#### Step 2
@ -47,6 +47,10 @@ $ react-native link
Done! All libraries with native dependencies should be successfully linked to your iOS/Android project.
> ***Note:*** If your iOS project is using CocoaPods (contains `Podfile`) and linked library has `podspec` file,
then `react-native link` will link library using Podfile. To support non-trivial Podfiles
add `# Add new pods below this line` comment to places where you expect pods to be added.
### Manual linking
#### Step 1

View File

@ -5,6 +5,7 @@ exports.valid = {
'demoProject.xcodeproj': {
'project.pbxproj': fs.readFileSync(path.join(__dirname, './files/project.pbxproj')),
},
'TestPod.podspec': 'empty'
};
exports.validTestName = {
@ -12,3 +13,7 @@ exports.validTestName = {
'project.pbxproj': fs.readFileSync(path.join(__dirname, './files/project.pbxproj')),
},
};
exports.pod = {
'TestPod.podspec': 'empty'
};

View File

@ -4,6 +4,7 @@ const ios = require('./ios');
const flat = {
android: android.valid,
ios: ios.valid,
Podfile: 'empty'
};
const nested = {
@ -19,4 +20,9 @@ const withExamples = {
android: android.valid,
};
module.exports = { flat, nested, withExamples };
const withPods = {
Podfile: 'content',
ios: ios.pod
};
module.exports = { flat, nested, withExamples, withPods };

View File

@ -0,0 +1,20 @@
'use strict';
jest.mock('fs');
const findPodfilePath = require('../../ios/findPodfilePath');
const fs = require('fs');
const projects = require('../../__fixtures__/projects');
const ios = require('../../__fixtures__/ios');
describe('ios::findPodfilePath', () => {
it('returns null if there is no Podfile', () => {
fs.__setMockFilesystem(ios.valid);
expect(findPodfilePath('')).toBeNull();
});
it('returns Podfile path if it exists', () => {
fs.__setMockFilesystem(projects.withPods);
expect(findPodfilePath('/ios')).toContain('Podfile');
});
});

View File

@ -0,0 +1,44 @@
'use strict';
jest.mock('fs');
const findPodspecName = require('../../ios/findPodspecName');
const fs = require('fs');
const projects = require('../../__fixtures__/projects');
const ios = require('../../__fixtures__/ios');
describe('ios::findPodspecName', () => {
it('returns null if there is not podspec file', () => {
fs.__setMockFilesystem(projects.flat);
expect(findPodspecName('')).toBeNull();
});
it('returns podspec name if only one exists', () => {
fs.__setMockFilesystem(ios.pod);
expect(findPodspecName('/')).toBe('TestPod');
});
it('returns podspec name that match packet directory', () => {
fs.__setMockFilesystem({
user: {
PacketName: {
'Another.podspec': 'empty',
'PacketName.podspec': 'empty'
}
}
});
expect(findPodspecName('/user/PacketName')).toBe('PacketName');
});
it('returns first podspec name if not match in directory', () => {
fs.__setMockFilesystem({
user: {
packet: {
'Another.podspec': 'empty',
'PacketName.podspec': 'empty'
}
}
});
expect(findPodspecName('/user/packet')).toBe('Another');
});
});

View File

@ -0,0 +1,11 @@
'use strict';
const fs = require('fs');
const path = require('path');
module.exports = function findPodfilePath(projectFolder) {
const podFilePath = path.join(projectFolder, '..', 'Podfile');
const podFileExists = fs.existsSync(podFilePath);
return podFileExists ? podFilePath : null;
};

View File

@ -0,0 +1,28 @@
'use strict';
const glob = require('glob');
const path = require('path');
module.exports = function findPodspecName(folder) {
const podspecs = glob.sync('*.podspec', { cwd: folder });
let podspecFile = null;
if (podspecs.length === 0) {
return null;
}
else if (podspecs.length === 1) {
podspecFile = podspecs[0];
}
else {
const folderParts = folder.split(path.sep);
const currentFolder = folderParts[folderParts.length - 1];
const toSelect = podspecs.indexOf(currentFolder + '.podspec');
if (toSelect === -1) {
podspecFile = podspecs[0];
}
else {
podspecFile = podspecs[toSelect];
}
}
return podspecFile.replace('.podspec', '');
};

View File

@ -9,6 +9,8 @@
'use strict';
const findProject = require('./findProject');
const findPodfilePath = require('./findPodfilePath');
const findPodspecName = require('./findPodspecName');
const path = require('path');
/**
@ -44,6 +46,8 @@ exports.projectConfig = function projectConfigIOS(folder, userConfig) {
sourceDir: path.dirname(projectPath),
folder: folder,
pbxprojPath: path.join(projectPath, 'project.pbxproj'),
podfile: findPodfilePath(projectPath),
podspec: findPodspecName(folder),
projectPath: projectPath,
projectName: path.basename(projectPath),
libraryFolder: userConfig.libraryFolder || 'Libraries',

View File

@ -0,0 +1,8 @@
source 'https://github.com/CocoaPods/Specs.git'
platform :ios, '9.0'
target 'Testing' do
pod 'TestPod', '~> 3.1'
# test should point to this line
end

View File

@ -0,0 +1,30 @@
source 'https://github.com/CocoaPods/Specs.git'
platform :ios, '9.0'
target 'none' do
pod 'React',
:path => "../node_modules/react-native",
:subspecs => [
"Core",
"ART",
"RCTActionSheet",
"RCTAnimation",
"RCTCameraRoll",
"RCTGeolocation",
"RCTImage",
"RCTNetwork",
"RCTText",
"RCTVibration",
"RCTWebSocket",
"DevSupport",
"BatchedBridge"
]
pod 'Yoga',
:path => "../node_modules/react-native/ReactCommon/yoga"
# test should point to this line
post_install do |installer|
end
end

View File

@ -0,0 +1,34 @@
source 'https://github.com/CocoaPods/Specs.git'
# platform :ios, '9.0'
target 'None' do
# Uncomment the next line if you're using Swift or would like to use dynamic frameworks
# use_frameworks!
# Your 'node_modules' directory is probably in the root of your project, # but if not, adjust the `:path` accordingly
pod 'React', :path => '../node_modules/react-native', :subspecs => [
'Core',
'RCTText',
'RCTNetwork',
'BatchedBridge',
'RCTImage',
'RCTWebSocket', # needed for debugging
# Add any other subspecs you want to use in your project
]
# Add new pods below this line
# test should point to this line
target 'NoneTests' do
inherit! :search_paths
# Pods for testing
end
end
target 'Second' do
target 'NoneUITests' do
inherit! :search_paths
# Add new pods below this line
end
end

View File

@ -0,0 +1,32 @@
source 'https://github.com/CocoaPods/Specs.git'
# platform :ios, '9.0'
target 'None' do
# Uncomment the next line if you're using Swift or would like to use dynamic frameworks
# use_frameworks!
# Your 'node_modules' directory is probably in the root of your project, # but if not, adjust the `:path` accordingly
pod 'React', :path => '../node_modules/react-native', :subspecs => [
'Core',
'RCTText',
'RCTNetwork',
'BatchedBridge',
'RCTImage',
'RCTWebSocket', # needed for debugging
# Add any other subspecs you want to use in your project
]
# Explicitly include Yoga if you are using RN >= 0.42.0
pod "Yoga", :path => "../node_modules/react-native/ReactCommon/yoga"
# test should point to this line
target 'NoneTests' do
inherit! :search_paths
# Pods for testing
end
target 'NoneUITests' do
inherit! :search_paths
# Pods for testing
end
end

View File

@ -0,0 +1,30 @@
'use strict';
const path = require('path');
const findLineToAddPod = require('../../pods/findLineToAddPod');
const readPodfile = require('../../pods/readPodfile');
const PODFILES_PATH = path.join(__dirname, '../../__fixtures__/pods');
const LINE_AFTER_TARGET_IN_TEST_PODFILE = 4;
describe('pods::findLineToAddPod', () => {
it('returns null if file is not Podfile', () => {
const podfile = readPodfile(path.join(PODFILES_PATH, '../Info.plist'));
expect(findLineToAddPod(podfile, LINE_AFTER_TARGET_IN_TEST_PODFILE)).toBeNull();
});
it('returns correct line number for Simple Podfile', () => {
const podfile = readPodfile(path.join(PODFILES_PATH, 'PodfileSimple'));
expect(findLineToAddPod(podfile, LINE_AFTER_TARGET_IN_TEST_PODFILE)).toEqual({ line: 7, indentation: 2 });
});
it('returns correct line number for Podfile with target', () => {
const podfile = readPodfile(path.join(PODFILES_PATH, 'PodfileWithTarget'));
expect(findLineToAddPod(podfile, LINE_AFTER_TARGET_IN_TEST_PODFILE)).toEqual({ line: 21, indentation: 2 });
});
it('returns correct line number for Podfile with function', () => {
const podfile = readPodfile(path.join(PODFILES_PATH, 'PodfileWithFunction'));
expect(findLineToAddPod(podfile, LINE_AFTER_TARGET_IN_TEST_PODFILE)).toEqual({ line: 26, indentation: 2 });
});
});

View File

@ -0,0 +1,26 @@
'use strict';
const path = require('path');
const readPodfile = require('../../pods/readPodfile');
const findMarkedLinesInPodfile = require('../../pods/findMarkedLinesInPodfile');
const PODFILES_PATH = path.join(__dirname, '../../__fixtures__/pods');
const LINE_AFTER_TARGET_IN_TEST_PODFILE = 4;
describe('pods::findMarkedLinesInPodfile', () => {
it('returns empty array if file is not Podfile', () => {
const podfile = readPodfile(path.join(PODFILES_PATH, '../Info.plist'));
expect(findMarkedLinesInPodfile(podfile)).toEqual([]);
});
it('returns empty array for Simple Podfile', () => {
const podfile = readPodfile(path.join(PODFILES_PATH, 'PodfileSimple'));
expect(findMarkedLinesInPodfile(podfile, LINE_AFTER_TARGET_IN_TEST_PODFILE)).toEqual([]);
});
it('returns correct line numbers for Podfile with marker', () => {
const podfile = readPodfile(path.join(PODFILES_PATH, 'PodfileWithMarkers'));
const expectedObject = [{ line: 18, indentation: 2 }, { line: 31, indentation: 4 }];
expect(findMarkedLinesInPodfile(podfile, LINE_AFTER_TARGET_IN_TEST_PODFILE)).toEqual(expectedObject);
});
});

View File

@ -0,0 +1,24 @@
'use strict';
const path = require('path');
const findPodTargetLine = require('../../pods/findPodTargetLine');
const readPodfile = require('../../pods/readPodfile');
const PODFILES_PATH = path.join(__dirname, '../../__fixtures__/pods');
describe('pods::findPodTargetLine', () => {
it('returns null if file is not Podfile', () => {
const podfile = readPodfile(path.join(PODFILES_PATH, '../Info.plist'));
expect(findPodTargetLine(podfile, 'name')).toBeNull();
});
it('returns null if there is not matching project name', () => {
const podfile = readPodfile(path.join(PODFILES_PATH, 'PodfileSimple'));
expect(findPodTargetLine(podfile, 'invalidName')).toBeNull();
});
it('returns null if there is not matching project name', () => {
const podfile = readPodfile(path.join(PODFILES_PATH, 'PodfileSimple'));
expect(findPodTargetLine(podfile, 'Testing')).toBe(4);
});
});

View File

@ -0,0 +1,32 @@
'use strict';
const path = require('path');
const isInstalled = require('../../pods/isInstalled');
const PODFILES_PATH = path.join(__dirname, '../../__fixtures__/pods');
describe('pods::isInstalled', () => {
it('returns false if pod is missing', () => {
const project = { podfile: path.join(PODFILES_PATH, 'PodfileSimple') };
const podspecName = { podspec: 'NotExisting' };
expect(isInstalled(project, podspecName)).toBe(false);
});
it('returns true for existing pod with version number', () => {
const project = { podfile: path.join(PODFILES_PATH, 'PodfileSimple') };
const podspecName = { podspec: 'TestPod' };
expect(isInstalled(project, podspecName)).toBe(true);
});
it('returns true for existing pod with path', () => {
const project = { podfile: path.join(PODFILES_PATH, 'PodfileWithTarget') };
const podspecName = { podspec: 'Yoga' };
expect(isInstalled(project, podspecName)).toBe(true);
});
it('returns true for existing pod with multiline definition', () => {
const project = { podfile: path.join(PODFILES_PATH, 'PodfileWithFunction') };
const podspecName = { podspec: 'React' };
expect(isInstalled(project, podspecName)).toBe(true);
});
});

View File

@ -0,0 +1,35 @@
'use strict';
const path = require('path');
const removePodEntry = require('../../pods/removePodEntry');
const readPodfile = require('../../pods/readPodfile');
const PODFILES_PATH = path.join(__dirname, '../../__fixtures__/pods');
describe('pods::removePodEntry', () => {
it('should remove one line from Podfile with TestPod', () => {
const { podfileContent, podLinesCount } = readTestPodFile('PodfileSimple');
const podFileWithRemoved = removePodEntry(podfileContent, 'TestPod');
const newLineCount = podFileWithRemoved.split('\n').length;
expect(newLineCount).toBe(podLinesCount - 1);
});
it('should remove one line from Podfile with Yoga', () => {
const { podfileContent, podLinesCount } = readTestPodFile('PodfileWithTarget');
const podFileWithRemoved = removePodEntry(podfileContent, 'Yoga');
const newLineCount = podFileWithRemoved.split('\n').length;
expect(newLineCount).toBe(podLinesCount - 1);
});
it('should remove whole reference to React pod from Podfile', () => {
const { podfileContent, podLinesCount } = readTestPodFile('PodfileWithTarget');
const podFileWithRemoved = removePodEntry(podfileContent, 'React');
const newLineCount = podFileWithRemoved.split('\n').length;
expect(newLineCount).toBe(podLinesCount - 9);
});
});
function readTestPodFile(fileName) {
const podfileLines = readPodfile(path.join(PODFILES_PATH, fileName));
return { podfileContent: podfileLines.join('\n'), podLinesCount: podfileLines.length };
}

View File

@ -29,9 +29,11 @@ const promiseWaterfall = require('./promiseWaterfall');
const registerDependencyAndroid = require('./android/registerNativeModule');
const registerDependencyWindows = require('./windows/registerNativeModule');
const registerDependencyIOS = require('./ios/registerNativeModule');
const registerDependencyPods = require('./pods/registerNativeModule');
const isInstalledAndroid = require('./android/isInstalled');
const isInstalledWindows = require('./windows/isInstalled');
const isInstalledIOS = require('./ios/isInstalled');
const isInstalledPods = require('./pods/isInstalled');
const copyAssetsAndroid = require('./android/copyAssets');
const copyAssetsIOS = require('./ios/copyAssets');
const getProjectDependencies = require('./getProjectDependencies');
@ -106,17 +108,19 @@ const linkDependencyIOS = (iOSProject, dependency) => {
return;
}
const isInstalled = isInstalledIOS(iOSProject, dependency.config.ios);
const isInstalled = isInstalledIOS(iOSProject, dependency.config.ios) || isInstalledPods(iOSProject, dependency.config.ios);
if (isInstalled) {
log.info(chalk.grey(`iOS module ${dependency.name} is already linked`));
return;
}
log.info(`Linking ${dependency.name} ios dependency`);
registerDependencyIOS(dependency.config.ios, iOSProject);
if (iOSProject.podfile && dependency.config.ios.podspec) {
registerDependencyPods(dependency, iOSProject);
}
else {
registerDependencyIOS(dependency.config.ios, iOSProject);
}
log.info(`iOS module ${dependency.name} has been successfully linked`);
};

View File

@ -0,0 +1,21 @@
'use strict';
module.exports = function addPodEntry(podLines, linesToAddEntry, podName, nodePath) {
const newEntry = `pod '${podName}', :path => '../node_modules/${nodePath}'\n`;
if (!linesToAddEntry) {
return;
} else if (Array.isArray(linesToAddEntry)) {
linesToAddEntry.map(({ line, indentation }, idx) =>
podLines.splice(line + idx, 0, getLineToAdd(newEntry, indentation))
);
} else {
const { line, indentation } = linesToAddEntry;
podLines.splice(line, 0, getLineToAdd(newEntry, indentation));
}
};
function getLineToAdd(newEntry, indentation) {
const spaces = Array(indentation + 1).join(' ');
return spaces + newEntry;
}

View File

@ -0,0 +1,24 @@
'use strict';
module.exports = function findLineToAddPod(podLines, firstTargetLine) {
// match line with new target: target 'project_name' do (most likely target inside podfile main target)
const nextTarget = /target (\'|\")\w+(\'|\") do/g;
// match line that has only 'end' (if we don't catch new target or function, this would mean this is end of current target)
const endOfCurrentTarget = /^\s*end\s*$/g;
// match function definition, like: post_install do |installer| (some Podfiles have function defined inside main target
const functionDefinition = /^\s*[a-z_]+\s+do(\s+\|[a-z]+\|)?/g;
for (let i = firstTargetLine, len = podLines.length; i < len; i++) {
const matchNextConstruct = podLines[i].match(nextTarget) || podLines[i].match(functionDefinition);
const matchEnd = podLines[i].match(endOfCurrentTarget);
if (matchNextConstruct || matchEnd) {
const firstNonSpaceCharacter = podLines[i].search(/\S/);
return {
indentation: firstNonSpaceCharacter + (matchEnd ? 2 : 0),
line: i
};
}
}
return null;
};

View File

@ -0,0 +1,12 @@
'use strict';
const MARKER_TEXT = '# Add new pods below this line';
module.exports = function findMarkedLinesInPodfile(podLines) {
const result = [];
for (let i = 0, len = podLines.length; i < len; i++) {
if (podLines[i].includes(MARKER_TEXT)) {
result.push({ line: i + 1, indentation: podLines[i].indexOf('#') });
}
}
return result;
};

View File

@ -0,0 +1,14 @@
'use strict';
module.exports = function findPodTargetLine(podLines, projectName) {
const targetName = projectName.replace('.xcodeproj', '');
//match first target definition in file: target 'target_name' do
const targetRegex = new RegExp('target (\'|\")' + targetName + '(\'|\") do', 'g');
for (let i = 0, len = podLines.length; i < len; i++) {
const match = podLines[i].match(targetRegex);
if (match) {
return i + 1;
}
}
return null;
};

View File

@ -0,0 +1,19 @@
'use strict';
const readPodfile = require('./readPodfile');
module.exports = function isInstalled(iOSProject, dependencyConfig) {
if (!iOSProject.podfile) {
return false;
}
// match line with pod declaration: pod 'dependencyPodName' (other possible parameters of pod are ignored)
const dependencyRegExp = new RegExp('pod\\s+(\'|\")' + dependencyConfig.podspec + '(\'|\")', 'g');
const podLines = readPodfile(iOSProject.podfile);
for (let i = 0, len = podLines.length; i < len; i++) {
const match = podLines[i].match(dependencyRegExp);
if (match) {
return true;
}
}
return false;
};

View File

@ -0,0 +1,8 @@
'use strict';
const fs = require('fs');
module.exports = function readPodfile(podfilePath) {
const podContent = fs.readFileSync(podfilePath, 'utf8');
return podContent.split(/\r?\n/g);
};

View File

@ -0,0 +1,25 @@
'use strict';
const readPodfile = require('./readPodfile');
const findPodTargetLine = require('./findPodTargetLine');
const findLineToAddPod = require('./findLineToAddPod');
const findMarkedLinesInPodfile = require('./findMarkedLinesInPodfile');
const addPodEntry = require('./addPodEntry');
const savePodFile = require('./savePodFile');
module.exports = function registerNativeModulePods(dependency, iOSProject) {
const podLines = readPodfile(iOSProject.podfile);
const linesToAddEntry = getLinesToAddEntry(podLines, iOSProject);
addPodEntry(podLines, linesToAddEntry, dependency.config.ios.podspec, dependency.name);
savePodFile(iOSProject.podfile, podLines);
};
function getLinesToAddEntry(podLines, { projectName }) {
const linesToAddPodWithMarker = findMarkedLinesInPodfile(podLines);
if (linesToAddPodWithMarker.length > 0) {
return linesToAddPodWithMarker;
} else {
const firstTargetLined = findPodTargetLine(podLines, projectName);
return findLineToAddPod(podLines, firstTargetLined);
}
}

View File

@ -0,0 +1,7 @@
'use strict';
module.exports = function removePodEntry(podfileContent, podName) {
// this regex should catch line(s) with full pod definition, like: pod 'podname', :path => '../node_modules/podname', :subspecs => ['Sub2', 'Sub1']
const podRegex = new RegExp("\\n( |\\t)*pod\\s+(\"|')" + podName + "(\"|')(,\\s*(:[a-z]+\\s*=>)?\\s*((\"|').*?(\"|')|\\[[\\s\\S]*?\\]))*\\n", 'g');
return podfileContent.replace(podRegex, '\n');
};

View File

@ -0,0 +1,8 @@
'use strict';
const fs = require('fs');
module.exports = function savePodFile(podfilePath, podLines) {
const newPodfile = podLines.join('\n');
fs.writeFileSync(podfilePath, newPodfile);
};

View File

@ -0,0 +1,13 @@
'use strict';
const fs = require('fs');
const removePodEntry = require('./removePodEntry');
/**
* Unregister native module IOS with CocoaPods
*/
module.exports = function unregisterNativeModule(dependencyConfig, iOSProject) {
const podContent = fs.readFileSync(iOSProject.podfile, 'utf8');
const removed = removePodEntry(podContent, dependencyConfig.podspec);
fs.writeFileSync(iOSProject.podfile, removed);
};

View File

@ -4,16 +4,17 @@ const getProjectDependencies = require('./getProjectDependencies');
const unregisterDependencyAndroid = require('./android/unregisterNativeModule');
const unregisterDependencyWindows = require('./windows/unregisterNativeModule');
const unregisterDependencyIOS = require('./ios/unregisterNativeModule');
const unregisterDependencyPods = require('./pods/unregisterNativeModule');
const isInstalledAndroid = require('./android/isInstalled');
const isInstalledWindows = require('./windows/isInstalled');
const isInstalledIOS = require('./ios/isInstalled');
const isInstalledPods = require('./pods/isInstalled');
const unlinkAssetsAndroid = require('./android/unlinkAssets');
const unlinkAssetsIOS = require('./ios/unlinkAssets');
const getDependencyConfig = require('./getDependencyConfig');
const compact = require('lodash').compact;
const difference = require('lodash').difference;
const filter = require('lodash').filter;
const find = require('lodash').find;
const flatten = require('lodash').flatten;
const isEmpty = require('lodash').isEmpty;
const promiseWaterfall = require('./promiseWaterfall');
@ -65,16 +66,21 @@ const unlinkDependencyIOS = (iOSProject, dependency, packageName, iOSDependencie
return;
}
const isInstalled = isInstalledIOS(iOSProject, dependency.ios);
if (!isInstalled) {
const isIosInstalled = isInstalledIOS(iOSProject, dependency.ios);
const isPodInstalled = isInstalledPods(iOSProject, dependency.ios);
if (!isIosInstalled && !isPodInstalled) {
log.info(`iOS module ${packageName} is not installed`);
return;
}
log.info(`Unlinking ${packageName} ios dependency`);
unregisterDependencyIOS(dependency.ios, iOSProject, iOSDependencies);
if (isIosInstalled) {
unregisterDependencyIOS(dependency.ios, iOSProject, iOSDependencies);
}
else if (isPodInstalled) {
unregisterDependencyPods(dependency.ios, iOSProject);
}
log.info(`iOS module ${packageName} has been successfully unlinked`);
};