Skip to content

Commit 4c196ae

Browse files
mironiastyide
authored andcommitted
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 #15460 Differential Revision: D6078649 Pulled By: hramos fbshipit-source-id: 9651085875892fd66299563ca0e42fb2bcc00825
1 parent 87bc732 commit 4c196ae

29 files changed

Lines changed: 547 additions & 13 deletions

docs/LinkingLibraries.md

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -35,8 +35,8 @@ Install a library with native dependencies:
3535
$ npm install <library-with-native-dependencies> --save
3636
```
3737

38-
**Note:** _`--save` or `--save-dev` flag is very important for this step. React Native will link
39-
your libs based on `dependencies` and `devDependencies` in your `package.json` file._
38+
> ***Note:*** `--save` or `--save-dev` flag is very important for this step. React Native will link
39+
your libs based on `dependencies` and `devDependencies` in your `package.json` file.
4040

4141
#### Step 2
4242

@@ -47,6 +47,10 @@ $ react-native link
4747

4848
Done! All libraries with native dependencies should be successfully linked to your iOS/Android project.
4949

50+
> ***Note:*** If your iOS project is using CocoaPods (contains `Podfile`) and linked library has `podspec` file,
51+
then `react-native link` will link library using Podfile. To support non-trivial Podfiles
52+
add `# Add new pods below this line` comment to places where you expect pods to be added.
53+
5054
### Manual linking
5155

5256
#### Step 1

local-cli/core/__fixtures__/ios.js

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,15 @@ exports.valid = {
55
'demoProject.xcodeproj': {
66
'project.pbxproj': fs.readFileSync(path.join(__dirname, './files/project.pbxproj')),
77
},
8+
'TestPod.podspec': 'empty'
89
};
910

1011
exports.validTestName = {
1112
'MyTestProject.xcodeproj': {
1213
'project.pbxproj': fs.readFileSync(path.join(__dirname, './files/project.pbxproj')),
1314
},
1415
};
16+
17+
exports.pod = {
18+
'TestPod.podspec': 'empty'
19+
};

local-cli/core/__fixtures__/projects.js

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ const ios = require('./ios');
44
const flat = {
55
android: android.valid,
66
ios: ios.valid,
7+
Podfile: 'empty'
78
};
89

910
const nested = {
@@ -19,4 +20,9 @@ const withExamples = {
1920
android: android.valid,
2021
};
2122

22-
module.exports = { flat, nested, withExamples };
23+
const withPods = {
24+
Podfile: 'content',
25+
ios: ios.pod
26+
};
27+
28+
module.exports = { flat, nested, withExamples, withPods };
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
'use strict';
2+
3+
jest.mock('fs');
4+
5+
const findPodfilePath = require('../../ios/findPodfilePath');
6+
const fs = require('fs');
7+
const projects = require('../../__fixtures__/projects');
8+
const ios = require('../../__fixtures__/ios');
9+
10+
describe('ios::findPodfilePath', () => {
11+
it('returns null if there is no Podfile', () => {
12+
fs.__setMockFilesystem(ios.valid);
13+
expect(findPodfilePath('')).toBeNull();
14+
});
15+
16+
it('returns Podfile path if it exists', () => {
17+
fs.__setMockFilesystem(projects.withPods);
18+
expect(findPodfilePath('/ios')).toContain('Podfile');
19+
});
20+
});
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
'use strict';
2+
3+
jest.mock('fs');
4+
5+
const findPodspecName = require('../../ios/findPodspecName');
6+
const fs = require('fs');
7+
const projects = require('../../__fixtures__/projects');
8+
const ios = require('../../__fixtures__/ios');
9+
10+
describe('ios::findPodspecName', () => {
11+
it('returns null if there is not podspec file', () => {
12+
fs.__setMockFilesystem(projects.flat);
13+
expect(findPodspecName('')).toBeNull();
14+
});
15+
16+
it('returns podspec name if only one exists', () => {
17+
fs.__setMockFilesystem(ios.pod);
18+
expect(findPodspecName('/')).toBe('TestPod');
19+
});
20+
21+
it('returns podspec name that match packet directory', () => {
22+
fs.__setMockFilesystem({
23+
user: {
24+
PacketName: {
25+
'Another.podspec': 'empty',
26+
'PacketName.podspec': 'empty'
27+
}
28+
}
29+
});
30+
expect(findPodspecName('/user/PacketName')).toBe('PacketName');
31+
});
32+
33+
it('returns first podspec name if not match in directory', () => {
34+
fs.__setMockFilesystem({
35+
user: {
36+
packet: {
37+
'Another.podspec': 'empty',
38+
'PacketName.podspec': 'empty'
39+
}
40+
}
41+
});
42+
expect(findPodspecName('/user/packet')).toBe('Another');
43+
});
44+
});
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
'use strict';
2+
3+
const fs = require('fs');
4+
const path = require('path');
5+
6+
module.exports = function findPodfilePath(projectFolder) {
7+
const podFilePath = path.join(projectFolder, '..', 'Podfile');
8+
const podFileExists = fs.existsSync(podFilePath);
9+
10+
return podFileExists ? podFilePath : null;
11+
};
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
'use strict';
2+
3+
const glob = require('glob');
4+
const path = require('path');
5+
6+
module.exports = function findPodspecName(folder) {
7+
const podspecs = glob.sync('*.podspec', { cwd: folder });
8+
let podspecFile = null;
9+
if (podspecs.length === 0) {
10+
return null;
11+
}
12+
else if (podspecs.length === 1) {
13+
podspecFile = podspecs[0];
14+
}
15+
else {
16+
const folderParts = folder.split(path.sep);
17+
const currentFolder = folderParts[folderParts.length - 1];
18+
const toSelect = podspecs.indexOf(currentFolder + '.podspec');
19+
if (toSelect === -1) {
20+
podspecFile = podspecs[0];
21+
}
22+
else {
23+
podspecFile = podspecs[toSelect];
24+
}
25+
}
26+
27+
return podspecFile.replace('.podspec', '');
28+
};

local-cli/core/ios/index.js

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@
99
'use strict';
1010

1111
const findProject = require('./findProject');
12+
const findPodfilePath = require('./findPodfilePath');
13+
const findPodspecName = require('./findPodspecName');
1214
const path = require('path');
1315

1416
/**
@@ -44,6 +46,8 @@ exports.projectConfig = function projectConfigIOS(folder, userConfig) {
4446
sourceDir: path.dirname(projectPath),
4547
folder: folder,
4648
pbxprojPath: path.join(projectPath, 'project.pbxproj'),
49+
podfile: findPodfilePath(projectPath),
50+
podspec: findPodspecName(folder),
4751
projectPath: projectPath,
4852
projectName: path.basename(projectPath),
4953
libraryFolder: userConfig.libraryFolder || 'Libraries',
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
source 'https://github.com/CocoaPods/Specs.git'
2+
platform :ios, '9.0'
3+
4+
target 'Testing' do
5+
pod 'TestPod', '~> 3.1'
6+
7+
# test should point to this line
8+
end
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
source 'https://github.com/CocoaPods/Specs.git'
2+
platform :ios, '9.0'
3+
4+
target 'none' do
5+
pod 'React',
6+
:path => "../node_modules/react-native",
7+
:subspecs => [
8+
"Core",
9+
"ART",
10+
"RCTActionSheet",
11+
"RCTAnimation",
12+
"RCTCameraRoll",
13+
"RCTGeolocation",
14+
"RCTImage",
15+
"RCTNetwork",
16+
"RCTText",
17+
"RCTVibration",
18+
"RCTWebSocket",
19+
"DevSupport",
20+
"BatchedBridge"
21+
]
22+
23+
pod 'Yoga',
24+
:path => "../node_modules/react-native/ReactCommon/yoga"
25+
26+
# test should point to this line
27+
post_install do |installer|
28+
29+
end
30+
end

0 commit comments

Comments
 (0)