Skip to content

Commit 61a4605

Browse files
author
Harsha Bonthu
committed
- Added hooks to update android manifest and generate app associate file for iOS
- Bugfix get params from deep links on iOS - Added Branch framework instead of lib/ lib folders
1 parent a30665d commit 61a4605

File tree

96 files changed

+1749
-7091
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

96 files changed

+1749
-7091
lines changed

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ There's a full demo app embedded in this repository. It should serve as an examp
2626
**Install parameters:**
2727
* `BRANCH_LIVE_KEY` - Your Branch live API key. You can sign up for your own Branch key at [https://dashboard.branch.io](https://dashboard.branch.io).
2828
* `URI_SCHEME` - It could be your app name or the URI set in your Branch dashboard. As a reminder, the URI scheme is what you use to open your app from a browser, i.e. `yourapp://`.
29-
* [optional] `ENCODED_ID` - This is for supporting App Links (6.0+) on Android. You can obtain the encodied id from the Branch dashboard. Just append `--variable ENCODED_ID=your-encoded-id` to the plugin install command below. For more info about App Links, please see [this](https://github.com/BranchMetrics/Android-Deferred-Deep-Linking-SDK/blob/master/README.md#leverage-android-app-links-for-deep-linking) section of the Android readme.
29+
* [optional] `ENCODED_ID` - This is for supporting App Links (6.0+) on Android. You can obtain the encodied id from the Branch dashboard. For more info about App Links, please see [this](https://github.com/BranchMetrics/Android-Deferred-Deep-Linking-SDK/blob/master/README.md#leverage-android-app-links-for-deep-linking) section of the Android readme.
3030

3131
#### Cordova
3232

hooks/afterPrepareHook.js

Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
/**
2+
Hook is executed at the end of the 'prepare' stage. Usually, when you call 'cordova build'.
3+
4+
It will inject required preferences in the platform-specific projects, based on <universal-links>
5+
data you have specified in the projects config.xml file.
6+
*/
7+
8+
var configParser = require('./lib/configXmlParser.js'),
9+
androidManifestWriter = require('./lib/android/manifestWriter.js'),
10+
// androidWebHook = require('./lib/android/webSiteHook.js'),
11+
iosProjectEntitlements = require('./lib/ios/projectEntitlements.js'),
12+
// iosAppSiteAssociationFile = require('./lib/ios/appleAppSiteAssociationFile.js'),
13+
iosProjectPreferences = require('./lib/ios/xcodePreferences.js'),
14+
ANDROID = 'android',
15+
IOS = 'ios';
16+
17+
module.exports = function(ctx) {
18+
run(ctx);
19+
};
20+
21+
/**
22+
* Execute hook.
23+
*
24+
* @param {Object} cordovaContext - cordova context object
25+
*/
26+
function run(cordovaContext) {
27+
var pluginPreferences = configParser.readPreferences(cordovaContext),
28+
platformsList = cordovaContext.opts.platforms;
29+
30+
// if no preferences are found - exit
31+
if (pluginPreferences == null) {
32+
return;
33+
}
34+
35+
// if no host is defined - exit
36+
if (pluginPreferences.hosts == null || pluginPreferences.hosts.length == 0) {
37+
console.warn('No host is specified in the config.xml. Universal Links plugin is not going to work.');
38+
return;
39+
}
40+
41+
platformsList.forEach(function(platform) {
42+
switch (platform) {
43+
case ANDROID:
44+
{
45+
activateUniversalLinksInAndroid(cordovaContext, pluginPreferences);
46+
break;
47+
}
48+
case IOS:
49+
{
50+
activateUniversalLinksInIos(cordovaContext, pluginPreferences);
51+
break;
52+
}
53+
}
54+
});
55+
}
56+
57+
/**
58+
* Activate Deep Links for Android application.
59+
*
60+
* @param {Object} cordovaContext - cordova context object
61+
* @param {Object} pluginPreferences - plugin preferences from the config.xml file. Basically, content from <universal-links> tag.
62+
*/
63+
function activateUniversalLinksInAndroid(cordovaContext, pluginPreferences) {
64+
// inject preferenes into AndroidManifest.xml
65+
androidManifestWriter.writePreferences(cordovaContext, pluginPreferences);
66+
67+
// generate html file with the <link> tags that you should inject on the website.
68+
// androidWebHook.generate(cordovaContext, pluginPreferences);
69+
}
70+
71+
/**
72+
* Activate Universal Links for iOS application.
73+
*
74+
* @param {Object} cordovaContext - cordova context object
75+
* @param {Object} pluginPreferences - plugin preferences from the config.xml file. Basically, content from <universal-links> tag.
76+
*/
77+
function activateUniversalLinksInIos(cordovaContext, pluginPreferences) {
78+
// modify xcode project preferences
79+
iosProjectPreferences.enableAssociativeDomainsCapability(cordovaContext);
80+
81+
// generate entitlements file
82+
iosProjectEntitlements.generateAssociatedDomainsEntitlements(cordovaContext, pluginPreferences);
83+
84+
// generate apple-site-association-file
85+
// iosAppSiteAssociationFile.generate(cordovaContext, pluginPreferences);
86+
}

hooks/beforePluginInstallHook.js

Lines changed: 154 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,154 @@
1+
/**
2+
Hook is executed when plugin is added to the project.
3+
It will check all necessary module dependencies and install the missing ones locally.
4+
*/
5+
6+
var exec = require('child_process').exec,
7+
path = require('path'),
8+
fs = require('fs'),
9+
INSTALLATION_FLAG_FILE_NAME = '.installed';
10+
11+
// region NPM specific
12+
13+
/**
14+
* Check if node package is installed.
15+
*
16+
* @param {String} moduleName
17+
* @return {Boolean} true if package already installed
18+
*/
19+
function isNodeModuleInstalled(moduleName) {
20+
var installed = true;
21+
try {
22+
var module = require(moduleName);
23+
} catch (err) {
24+
installed = false;
25+
}
26+
27+
return installed;
28+
}
29+
30+
/**
31+
* Install node module locally.
32+
* Basically, it runs 'npm install module_name'.
33+
*
34+
* @param {String} moduleName
35+
* @param {Callback(error)} callback
36+
*/
37+
function installNodeModule(moduleName, callback) {
38+
if (isNodeModuleInstalled(moduleName)) {
39+
printLog('Node module ' + moduleName + ' is found');
40+
callback(null);
41+
return;
42+
}
43+
printLog('Can\'t find module ' + moduleName + ', running npm install');
44+
45+
var cmd = 'npm install -D ' + moduleName;
46+
exec(cmd, function(err, stdout, stderr) {
47+
callback(err);
48+
});
49+
}
50+
51+
/**
52+
* Install all required node packages.
53+
*/
54+
function installRequiredNodeModules(modulesToInstall) {
55+
if (!modulesToInstall.length) {
56+
return;
57+
}
58+
59+
var moduleName = modulesToInstall.shift();
60+
installNodeModule(moduleName, function(err) {
61+
if (err) {
62+
printLog('Failed to install module ' + moduleName + ':' + err);
63+
return;
64+
}
65+
66+
printLog('Module ' + moduleName + ' is installed');
67+
installRequiredNodeModules(modulesToInstall);
68+
});
69+
}
70+
71+
// endregion
72+
73+
// region Logging
74+
75+
function logStart() {
76+
console.log('Checking dependencies:');
77+
}
78+
79+
function printLog(msg) {
80+
var formattedMsg = ' ' + msg;
81+
console.log(formattedMsg);
82+
}
83+
84+
// endregion
85+
86+
// region Private API
87+
88+
/**
89+
* Check if we already executed this hook.
90+
*
91+
* @param {Object} ctx - cordova context
92+
* @return {Boolean} true if already executed; otherwise - false
93+
*/
94+
function isInstallationAlreadyPerformed(ctx) {
95+
var pathToInstallFlag = path.join(ctx.opts.projectRoot, 'plugins', ctx.opts.plugin.id, INSTALLATION_FLAG_FILE_NAME),
96+
isInstalled = false;
97+
try {
98+
var content = fs.readFileSync(pathToInstallFlag);
99+
isInstalled = true;
100+
} catch (err) {
101+
}
102+
103+
return isInstalled;
104+
}
105+
106+
/**
107+
* Create empty file - indicator, that we tried to install dependency modules after installation.
108+
* We have to do that, or this hook is gonna be called on any plugin installation.
109+
*/
110+
function createPluginInstalledFlag(ctx) {
111+
var pathToInstallFlag = path.join(ctx.opts.projectRoot, 'plugins', ctx.opts.plugin.id, INSTALLATION_FLAG_FILE_NAME);
112+
113+
fs.closeSync(fs.openSync(pathToInstallFlag, 'w'));
114+
}
115+
116+
// endregion
117+
118+
/**
119+
* Read dependencies from the package.json.
120+
* We will install them on the next step.
121+
*
122+
* @param {Object} ctx - cordova context
123+
* @return {Array} list of modules to install
124+
*/
125+
function readDependenciesFromPackageJson(ctx) {
126+
var data = require(path.join(ctx.opts.projectRoot, 'plugins', ctx.opts.plugin.id, 'package.json')),
127+
dependencies = data['dependencies'],
128+
modules = [];
129+
130+
if (!dependencies) {
131+
return modules;
132+
}
133+
134+
for (var module in dependencies) {
135+
modules.push(module);
136+
}
137+
138+
return modules;
139+
}
140+
141+
// hook's entry point
142+
module.exports = function(ctx) {
143+
// exit if we already executed this hook once
144+
if (isInstallationAlreadyPerformed(ctx)) {
145+
return;
146+
}
147+
148+
logStart();
149+
150+
var modules = readDependenciesFromPackageJson(ctx);
151+
installRequiredNodeModules(modules);
152+
153+
createPluginInstalledFlag(ctx);
154+
};

hooks/iosBeforePrepareHook.js

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
/*
2+
Hook executed before the 'prepare' stage. Only for iOS project.
3+
It will check if project name has changed. If so - it will change the name of the .entitlements file to remove that file duplicates.
4+
If file name has no changed - hook would not do anything.
5+
*/
6+
7+
var path = require('path'),
8+
fs = require('fs'),
9+
ConfigXmlHelper = require('./lib/configXmlHelper.js');
10+
11+
module.exports = function(ctx) {
12+
run(ctx);
13+
};
14+
15+
/**
16+
* Run the hook logic.
17+
*
18+
* @param {Object} ctx - cordova context object
19+
*/
20+
function run(ctx) {
21+
var projectRoot = ctx.opts.projectRoot,
22+
iosProjectFilePath = path.join(projectRoot, 'platforms', 'ios'),
23+
configXmlHelper = new ConfigXmlHelper(ctx),
24+
oldProjectName = getOldProjectName(iosProjectFilePath),
25+
newProjectName = configXmlHelper.getProjectName();
26+
27+
// if name has not changed - do nothing
28+
if (oldProjectName.length > 0 && oldProjectName === newProjectName) {
29+
return;
30+
}
31+
32+
console.log('Project name has changed. Renaming .entitlements file.');
33+
34+
// if it does - rename it
35+
var oldEntitlementsFilePath = path.join(iosProjectFilePath, oldProjectName, 'Resources', oldProjectName + '.entitlements'),
36+
newEntitlementsFilePath = path.join(iosProjectFilePath, oldProjectName, 'Resources', newProjectName + '.entitlements');
37+
38+
try {
39+
fs.renameSync(oldEntitlementsFilePath, newEntitlementsFilePath);
40+
} catch (err) {
41+
console.warn('Failed to rename .entitlements file.');
42+
console.warn(err);
43+
}
44+
}
45+
46+
// region Private API
47+
48+
/**
49+
* Get old name of the project.
50+
* Name is detected by the name of the .xcodeproj file.
51+
*
52+
* @param {String} projectDir absolute path to ios project directory
53+
* @return {String} old project name
54+
*/
55+
function getOldProjectName(projectDir) {
56+
var files = [],
57+
projectName = '';
58+
59+
try {
60+
files = fs.readdirSync(projectDir);
61+
} catch (err) {
62+
return '';
63+
}
64+
65+
// find file with .xcodeproj extension, use it as an old project name
66+
files.some(function(fileName) {
67+
if (path.extname(fileName) === '.xcodeproj') {
68+
projectName = path.basename(fileName, '.xcodeproj');
69+
return true;
70+
}
71+
72+
return false;
73+
});
74+
75+
return projectName;
76+
}
77+
78+
// endregion

0 commit comments

Comments
 (0)