diff --git a/hooks/apns.entitlements b/hooks/apns.entitlements
new file mode 100644
index 0000000..92267aa
--- /dev/null
+++ b/hooks/apns.entitlements
@@ -0,0 +1,8 @@
+
+
+
+
+ aps-environment
+ development
+
+
diff --git a/hooks/common.js b/hooks/common.js
new file mode 100644
index 0000000..c5852d3
--- /dev/null
+++ b/hooks/common.js
@@ -0,0 +1,85 @@
+/*
+ * Author: Derek Chia
+ * common functions for cordova plugin after hook
+ */
+const fs = require('fs');
+const path = require('path');
+
+module.exports.addAPNSinEntitlements = (entitlementPath, isProduction) => {
+ fs.readFile(entitlementPath, "utf8", function(err, data) {
+ if (err) {
+ throw err;
+ }
+
+ console.log("Reading entitlements file asynchronously");
+
+ let toInsert = 'aps-environment\n' +
+ '\t\tdevelopment';
+ if(isProduction) {
+ toInsert = 'aps-environment\n' +
+ '\t\tproduction';
+ }
+
+ let re1 = new RegExp('aps-environment<\/key>(.|[\r\n])*.*<\/string>');
+ let matched = data.match(re1);
+ let result;
+ if (matched === null) {
+ if(data.match(/<\/dict>/g)) {
+ result = data.replace(/<\/dict>/, '\t' + toInsert + '\n\t');
+ } else if(data.match(//g)) {
+ result = data.replace(//, '\t\n\t\t' + toInsert + '\n\t');
+ }
+ } else {
+ result = data.replace(re1, toInsert);
+ }
+
+ // write result to entitlements file
+ fs.writeFile(entitlementPath, result, {"encoding": 'utf8'}, function(err) {
+ if (err) {
+ throw err;
+ }
+ console.log(entitlementPath + " written successfully");
+ });
+ });
+}
+
+module.exports.removeAPNSinEntitlements = (entitlementPath) => {
+ fs.readFile(entitlementPath, "utf8", function(err, data) {
+ if (err) {
+ throw err;
+ }
+
+ console.log("Reading entitlements file asynchronously");
+
+ let re1 = new RegExp('aps-environment<\/key>(.|[\r\n])*.*<\/string>');
+ let matched = data.match(re1);
+ let result;
+ if (matched != null) {
+ result = data.replace(re1, "");
+ }
+
+ // write result to entitlements file
+ fs.writeFile(entitlementPath, result, {"encoding": 'utf8'}, function(err) {
+ if (err) {
+ throw err;
+ }
+ console.log(entitlementPath + " written successfully");
+ });
+ });
+}
+
+module.exports.getXcodeProjName = (searchPath) => {
+ if(searchPath == null || searchPath == undefined) {
+ searchPath = './';
+ }
+ let resultFolderName = null;
+ let folderNames = fs.readdirSync(searchPath).filter(file => fs.lstatSync(path.join(searchPath, file)).isDirectory());
+ let folderNamesReg = new RegExp('.*\.xcodeproj', 'g') // get filder name like `*.xcodeproj`
+ for(let folderName of folderNames) {
+ if(folderName.match(folderNamesReg)) {
+ resultFolderName = folderName;
+ break;
+ }
+ }
+ return resultFolderName.substr(0, resultFolderName.length - 10);
+}
diff --git a/hooks/iosDisablePush.js b/hooks/iosDisablePush.js
new file mode 100644
index 0000000..26add98
--- /dev/null
+++ b/hooks/iosDisablePush.js
@@ -0,0 +1,64 @@
+/*
+ * Author: Derek Chia
+ * Cordova plugin after hook to disable `Push Notification` capability for XCode 8
+ */
+
+const fs = require('fs');
+const path = require('path');
+let commonFuncs = require('./common');
+
+/**
+ * remove APNS env from cordova project Entitlements-Debug.plist and Entitlements-Release.plist
+ * This two file will work when xcode archive app
+ */
+let disablePushNotificationForCI = (basePath, xcodeprojName) => {
+ commonFuncs.removeAPNSinEntitlements(basePath + xcodeprojName + '/Entitlements-Debug.plist');
+ commonFuncs.removeAPNSinEntitlements(basePath + xcodeprojName + '/Entitlements-Release.plist');
+}
+
+/**
+ * remove APNS env to entitlement file; disable Push Notification capability in .pbxproj file
+ * This two file will work when xcode archive app
+ */
+let disablePushNotificationForXCode = (entitlementsPath, pbxprojPath) => {
+ /**
+ * remove APNS env to entitlement file
+ */
+ if( fs.existsSync(entitlementsPath) ) {
+ commonFuncs.removeAPNSinEntitlements(entitlementsPath);
+ }
+
+ /**
+ * disable Push Notification capability in .pbxproj file
+ * equally disable "Push Notification" switch in xcode
+ */
+ fs.readFile(pbxprojPath, "utf8", function(err, data) {
+ if (err) {
+ throw err;
+ }
+ console.log("Reading pbxproj file asynchronously");
+
+ // turn off Push Notification Capability
+ let re4rep = new RegExp('isa = PBXProject;(.|[\r\n])*TargetAttributes(.|[\r\n])*SystemCapabilities(.|[\r\n])*com\.apple\.Push = {(.|[\r\n])*enabled = [01]');
+ let parts = re4rep.exec(data);
+ result = data.replace(re4rep, parts[0].substr(0, parts[0].length - 1) + '0');
+
+ // write result to project.pbxproj
+ fs.writeFile(pbxprojPath, result, {"encoding": 'utf8'}, function(err) {
+ if (err) {
+ throw err;
+ }
+ console.log(pbxprojPath + " written successfully");
+ });
+ });
+}
+
+let basePath = './platforms/ios/';
+let buildType = 'dev';
+let xcodeprojName = commonFuncs.getXcodeProjName(basePath);
+let pbxprojPath = basePath + xcodeprojName + '.xcodeproj/project.pbxproj';
+let entitlementsPath = basePath + xcodeprojName + '/' + xcodeprojName + '.entitlements';
+
+disablePushNotificationForCI(basePath, xcodeprojName);
+
+disablePushNotificationForXCode(entitlementsPath, pbxprojPath);
\ No newline at end of file
diff --git a/hooks/iosEnablePush.js b/hooks/iosEnablePush.js
new file mode 100644
index 0000000..193b38e
--- /dev/null
+++ b/hooks/iosEnablePush.js
@@ -0,0 +1,167 @@
+/*
+ * Author: Derek Chia
+ * Cordova plugin after hook to enable `Push Notification` capability for XCode 8
+ */
+
+const fs = require('fs');
+const path = require('path');
+let commonFuncs = require('./common');
+
+/**
+ * add APNS env to cordova project Entitlements-Debug.plist and Entitlements-Release.plist
+ * This two file will work when xcode archive app
+ */
+let enablePushNotificationForCI = (basePath, xcodeprojName) => {
+ commonFuncs.addAPNSinEntitlements(basePath + xcodeprojName + '/Entitlements-Debug.plist', false);
+ commonFuncs.addAPNSinEntitlements(basePath + xcodeprojName + '/Entitlements-Release.plist', true);
+}
+
+/**
+ * add APNS env to entitlement file; enable Push Notification capability in .pbxproj file
+ * This two file will work when xcode archive app
+ */
+let enablePushNotificationForXCode = (entitlementsPath, pbxprojPath, cordovaBuildConfig) => {
+ console.log('will enable push notification capability for XCode');
+ let needAddEntitlementToPbxproj = false;
+ /**
+ * add APNS env to entitlement file
+ * without this file will cause a worning in xcode
+ */
+ if( fs.existsSync(entitlementsPath) ) {
+ commonFuncs.addAPNSinEntitlements(entitlementsPath, false);
+ } else {
+ // copy default entitlements file
+ fs.readFile(__dirname + '/apns.entitlements', 'utf8', function(err, data) {
+ if (err) {
+ throw err;
+ }
+
+ fs.writeFileSync(entitlementsPath, data);
+ console.log(entitlementsPath + " written successfully");
+ });
+
+ needAddEntitlementToPbxproj = true;
+ }
+
+ /**
+ * enable Push Notification capability in .pbxproj file
+ * equally enable "Push Notification" switch in xcode
+ */
+ fs.readFile(pbxprojPath, "utf8", function(err, data) {
+ if (err) {
+ throw err;
+ }
+ console.log("Reading pbxproj file asynchronously");
+
+ // add Push Notification Capability
+ let re1 = new RegExp('isa = PBXProject;(.|[\r\n])*TargetAttributes', 'g');
+ let re1rep = new RegExp('isa = PBXProject;(.|[\r\n])*attributes = {', 'g');
+ let re2 = new RegExp('(?:isa = PBXProject;(.|[\r\n])*TargetAttributes(.|[\r\n])*)SystemCapabilities', 'g');
+ let re2rep = new RegExp('isa = PBXProject;(.|[\r\n])*TargetAttributes = {', 'g');
+ let re3 = new RegExp('(?:isa = PBXProject;(.|[\r\n])*TargetAttributes(.|[\r\n])*SystemCapabilities(.|[\r\n])*)com\.apple\.Push', 'g');
+ let re3rep = new RegExp('isa = PBXProject;(.|[\r\n])*TargetAttributes(.|[\r\n])*SystemCapabilities = {', 'g');
+ let re4rep = new RegExp('isa = PBXProject;(.|[\r\n])*TargetAttributes(.|[\r\n])*SystemCapabilities(.|[\r\n])*com\.apple\.Push = {(.|[\r\n])*enabled = [01]');
+
+ let matched = data.match(re1);
+ let result;
+ if (matched === null) {
+ console.log('re1 not match, no TargetAttributes');
+ result = data.replace(re1rep, 'isa = PBXProject;\n' +
+ '\t\t\tattributes = {\n' +
+ '\t\t\t\tTargetAttributes = {\n' +
+ '\t\t\t\t\t1D6058900D05DD3D006BFB54 = {\n' +
+ '\t\t\t\t\t\tDevelopmentTeam = ' + cordovaBuildConfig.ios.release.developmentTeam + ';\n' +
+ '\t\t\t\t\t\tSystemCapabilities = {\n' +
+ '\t\t\t\t\t\t\tcom.apple.Push = {\n' +
+ '\t\t\t\t\t\t\t\tenabled = 1;\n' +
+ '\t\t\t\t\t\t\t};\n' +
+ '\t\t\t\t\t\t};\n' +
+ '\t\t\t\t\t};\n' +
+ '\t\t\t\t};');
+ } else {
+ matched = data.match(re2);
+ if(matched === null) {
+ console.log('re2 not match, nothing under TargetAttributes');
+ let parts = re2rep.exec(data);
+ result = data.replace(re2rep, parts[0] + '\n' + '\t\t\t\t\t1D6058900D05DD3D006BFB54 = {\n' +
+ '\t\t\t\t\t\tDevelopmentTeam = ' + cordovaBuildConfig.ios.release.developmentTeam + ';\n' +
+ '\t\t\t\t\t\tSystemCapabilities = {\n' +
+ '\t\t\t\t\t\t\tcom.apple.Push = {\n' +
+ '\t\t\t\t\t\t\t\tenabled = 1;\n' +
+ '\t\t\t\t\t\t\t};\n' +
+ '\t\t\t\t\t\t};\n' +
+ '\t\t\t\t\t};');
+ } else {
+ matched = data.match(re3);
+ if(matched === null) {
+ console.log('re3 not match, no com.apple.Push defined');
+ let parts = re3rep.exec(data);
+ result = data.replace(re3rep, parts[0] + '\n' + '\t\t\t\t\t\t\tcom.apple.Push = {\n' +
+ '\t\t\t\t\t\t\t\tenabled = 1;\n' +
+ '\t\t\t\t\t\t\t};');
+ } else {
+ console.log('just enable com.apple.Push');
+ let parts = re4rep.exec(data);
+ result = data.replace(re4rep, parts[0].substr(0, parts[0].length - 1) + '1');
+ }
+ }
+ }
+
+ // add entitlements
+ if (needAddEntitlementToPbxproj) {
+
+ let pathArray = entitlementsPath.split("/");
+ let entitlementsFileName = pathArray[pathArray.length - 1];
+ let projectFolderName = pathArray[pathArray.length - 2];
+
+ result = result.replace(new RegExp('\\/\\* Begin PBXFileReference section \\*\\/'), '/* Begin PBXFileReference section */\n' +
+ '\t\tD7BB385F1E4DB54800345BF4 /* ' + entitlementsFileName + ' */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; name = "' + entitlementsFileName + '"; path = "' + projectFolderName + '/' + entitlementsFileName + '"; sourceTree = ""; };');
+ result = result.replace(new RegExp('\\/\\* CustomTemplate \\*\\/.*\n.*isa = PBXGroup;.*\n.*children = \\('), '/* CustomTemplate */ = {\n' +
+ '\t\t\tisa = PBXGroup;\n' +
+ '\t\t\tchildren = (\n' +
+ '\t\t\t\tD7BB385F1E4DB54800345BF4 /* ' + entitlementsFileName + ' */,');
+ let re5rep = new RegExp('\\/\\* Debug \\*\\/.*\n.*isa = XCBuildConfiguration;.*\n.*\n.*buildSettings = {');
+ let parts = result.match(re5rep);
+ result = result.replace(re5rep, parts + '\n\t\t\t\tCODE_SIGN_ENTITLEMENTS = "' + projectFolderName + '/' + entitlementsFileName + '";');
+
+ let re6rep = new RegExp('\\/\\* Release \\*\\/.*\n.*isa = XCBuildConfiguration;.*\n.*\n.*buildSettings = {');
+ parts = result.match(re6rep);
+ result = result.replace(re6rep, parts + '\n\t\t\t\tCODE_SIGN_ENTITLEMENTS = "' + projectFolderName + '/' + entitlementsFileName + '";');
+ }
+
+ // write result to project.pbxproj
+ fs.writeFile(pbxprojPath, result, {"encoding": 'utf8'}, function(err) {
+ if (err) {
+ throw err;
+ }
+ console.log(pbxprojPath + " written successfully");
+ });
+ });
+}
+
+let basePath = './platforms/ios/';
+let buildType = 'dev';
+let xcodeprojName = commonFuncs.getXcodeProjName(basePath);
+let pbxprojPath = basePath + xcodeprojName + '.xcodeproj/project.pbxproj';
+let entitlementsPath = basePath + xcodeprojName + '/' + xcodeprojName + '.entitlements';
+
+let cordovaBuildConfigPath = './build.json'
+let cordovaBuildConfig = null;
+let willEnablePushNotificationForXCode = true;
+try { // try to read ios developmentTeam from build.json
+ cordovaBuildConfig = JSON.parse(fs.readFileSync(cordovaBuildConfigPath, "utf8"));
+} catch(e) {
+ console.log("Do not detected 'build.json' to get ios developent team. \n" +
+ "Will not enable XCode Push Notification Capability. \n" +
+ "Will only enable Push Notification for CI by add config to '" + basePath + xcodeprojName + "/Entitlements-Debug.plist' and '" + basePath + xcodeprojName + "/Entitlements-Release.plist' \n" +
+ "Please add 'build.json' to cordova project root folder to make after hook fullly functional. \n" +
+ "Reference [1]https://cordova.apache.org/docs/en/latest/reference/cordova-cli/#cordova-build-command \n" +
+ "Reference [2]https://cordova.apache.org/docs/en/latest/guide/platforms/ios/#signing-an-app");
+ willEnablePushNotificationForXCode = false;
+}
+
+enablePushNotificationForCI(basePath, xcodeprojName);
+
+if(willEnablePushNotificationForXCode) {
+ enablePushNotificationForXCode(entitlementsPath, pbxprojPath, cordovaBuildConfig);
+}
diff --git a/plugin.xml b/plugin.xml
index 916fc6f..0935590 100644
--- a/plugin.xml
+++ b/plugin.xml
@@ -57,6 +57,10 @@
$APP_KEY
+
+
+
+
@@ -155,7 +159,7 @@