Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Automate "Notification Service Extension" (File > New > Target) step #47

Open
rricamar opened this issue Mar 28, 2019 · 8 comments
Open

Comments

@rricamar
Copy link

rricamar commented Mar 28, 2019

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 (apache/cordova-ios#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 👍

@brad
Copy link

brad commented Apr 9, 2019

@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!

@adam-govan
Copy link

adam-govan commented May 29, 2019

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?

@brodycj
Copy link

brodycj commented Sep 27, 2019

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.

@adam-govan
Copy link

adam-govan commented Oct 10, 2019

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 👍

@brodycj

This comment has been minimized.

@adam-govan

This comment has been minimized.

@brodycj
Copy link

brodycj commented Oct 10, 2019

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).

@tkou15
Copy link

tkou15 commented Oct 18, 2023

#47 (comment)
Does this script still work?
It doesn't seem to work with Xcode15, node-xcode v3.0.1.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

5 participants