cordova-node-xcode
cordova-node-xcode copied to clipboard
Automate "Notification Service Extension" (File > New > Target) step
Hi ✋ ,
I'm working with a Cordova based App, and I would like to automate the "Notification Service Extension" step (In Xcode: File > New > Target) (for example, in Jenkins pipeline) , since I saw that after doing it, remove platform, and add it again, the associated target is removed 😭
I've been searching through the docs, stack overflow and another issues and I haven't seen anything... (and trying to make a diff between platform without/with the extension seems impossible)... is this possible / or somebody can provide an example of something similar (since my knowledge over xcode is very limited) ?
Thanks a lot in advance!
PS: actually this is duplicate from (https://github.com/apache/cordova-ios/issues/582) because I didn't know the existence of this project, if you think that the issue in the cordova-ios project should be closed let me know 🙏
PS: meanwhile I'm going to try myself with the library, if I achieve something I'll post here the solution for helping 👍
@rricamar I did it with a after_platform_add
hook like this:
const fs = require('fs');
const xcode = require('xcode');
module.exports = function(context) {
console.log('Starting hook to add notification extension to project');
const cordovaCommon = context.requireCordovaModule('cordova-common');
const appConfig = new cordovaCommon.ConfigParser('config.xml');
const appName = appConfig.name();
const iosPath = 'platforms/ios/';
const projPath = `${iosPath}${appName}.xcodeproj/project.pbxproj`;
const extName = 'MyNotificationServiceExtension';
const extFiles = [
'NotificationService.h',
'NotificationService.m',
`${extName}-Info.plist`,
];
// The directory where the source extension files are stored
const sourceDir = `src/extensions/${extName}/`;
// Wait a few seconds before parsing the project to let some other
// asynchronous project file changes complete. Maybe there is a way to get
// a promise?
console.log('Waiting a few seconds for other project file changes to finish');
setTimeout(function () {
console.log(`Adding ${extName} notification extension to ${appName}`);
let proj = xcode.project(projPath);
proj.parse(function (err) {
if (err) {
console.log(`Error parsing iOS project: ${err}`);
}
// Copy in the extension files
console.log('Copying in the extension files to the iOS project');
fs.mkdirSync(`${iosPath}${extName}`);
extFiles.forEach(function (extFile) {
let targetFile = `${iosPath}${extName}/${extFile}`;
fs.createReadStream(`${sourceDir}${extFile}`)
.pipe(fs.createWriteStream(targetFile));
});
// Create new PBXGroup for the extension
console.log('Creating new PBXGroup for the extension');
let extGroup = proj.addPbxGroup(extFiles, extName, extName);
// Add the new PBXGroup to the CustomTemplate group. This makes the
// files appear in the file explorer in Xcode.
console.log('Adding new PBXGroup to CustomTemplate PBXGroup');
let groups = proj.hash.project.objects['PBXGroup'];
Object.keys(groups).forEach(function (key) {
if (groups[key].name === 'CustomTemplate') {
proj.addToPbxGroup(extGroup.uuid, key);
}
});
// Add a target for the extension
console.log('Adding the new target');
let target = proj.addTarget(extName, 'app_extension');
// Add build phases to the new target
console.log('Adding build phases to the new target');
proj.addBuildPhase([ 'NotificationService.m' ], 'PBXSourcesBuildPhase', 'Sources', target.uuid);
proj.addBuildPhase([], 'PBXResourcesBuildPhase', 'Resources', target.uuid);
proj.addBuildPhase([], 'PBXFrameworksBuildPhase', 'Frameworks', target.uuid);
console.log('Write the changes to the iOS project file');
fs.writeFileSync(projPath, proj.writeSync());
console.log(`Added ${extName} notification extension to project`);
});
}, 3000);
};
The xcode
library gives you a ton a control to modify the project file. Hope this helps!
this is a great solution so far, but one problem I was running into recently was adding my own custom .framework
file to the extension.
I've tried the following code after the target was added your above solution:
// Adding Custom Framework
var opt = {target: target.uuid, embed: true, customFramework: true}
proj.addFramework(`${path_to_custom_framework}`, opt);
the path_to_custom_framework
is a local relative path which I'm confident is right since it's used for the main app as well and is correctly being embedded there. It's just on the Extension. what's really strange is the xcode project doesn't show any shell of a framework or anything, it simply has no changes and when I inspect the .framework in the main. It only has target membership for the main app and not the extension.
has anyone gotten past this point and actually added a custom .framework file to their service extension?
I am adding the bug and help wanted labels for now. Maintainers are completely overloaded at this point. Contribution of PR with test coverage in the test suite would be much appreciated.
hey I don't know if it's still relevant to everyone here but myself and the team I work with managed to get it all working with hooks as part of a plugin I work on.
The function iosSetupServiceExtension()
inside the ios_after_prepare.js script of the hooks section of the plugin edits the framework paths or an extension and adds a custom framework (we specify one but you could sub in your own at that point).
p.s. feedback on this after_prepare
or even the plugin is totally welcome since we're always trying to improve it 👍
Hi @adam-govan the "cordova-sdk" links you gave do not seem to work.
I personally don't have much time to look at this right now, PR would be welcome. Documentation PRs might help others as well.
whoops, I was pointing to the wrong repo @brodybits , they should be working now 👍
a plugin I work on
https://github.com/Swrve/swrve-cordova-sdk for quick & easy reference
Looks nice, I hope I get a chance to take a better look at it (someday).
https://github.com/apache/cordova-node-xcode/issues/47#issuecomment-481370180 Does this script still work? It doesn't seem to work with Xcode15, node-xcode v3.0.1.