I applied a workaround until it is supported by Cordova plugin.xml, I hope in the future, as soon as the embed property in such records will have the same effect: <framework embed="true" src="..." /> , at the moment this property does not help, therefore, the following workaround.
The following solution worked using Cordoba version 5.3.3.
First, be sure to add the frame entry in plugin.xml:
<framework src="pointToYour/File.framework" embed="true" />
embed="true" does not work, but add it anyway.
We will create a hook, declare that in your plugin.xml:
<hook type="after_platform_add" src="hooks/embedframework/addEmbedded.js" />
Next, there is a special node module that we will need in our hook code, this node-xcode module .
Install node -xcode (must be version 0.8.7 or higher):
npm i xcode
Finally, the hook code itself is
addEmbedded.js file:
'use strict'; const xcode = require('xcode'), fs = require('fs'), path = require('path'); module.exports = function(context) { if(process.length >=5 && process.argv[1].indexOf('cordova') == -1) { if(process.argv[4] != 'ios') { return; // plugin only meant to work for ios platform. } } function fromDir(startPath,filter, rec, multiple){ if (!fs.existsSync(startPath)){ console.log("no dir ", startPath); return; } const files=fs.readdirSync(startPath); var resultFiles = [] for(var i=0;i<files.length;i++){ var filename=path.join(startPath,files[i]); var stat = fs.lstatSync(filename); if (stat.isDirectory() && rec){ fromDir(filename,filter); //recurse } if (filename.indexOf(filter)>=0) { if (multiple) { resultFiles.push(filename); } else { return filename; } } } if(multiple) { return resultFiles; } } function getFileIdAndRemoveFromFrameworks(myProj, fileBasename) { var fileId = ''; const pbxFrameworksBuildPhaseObjFiles = myProj.pbxFrameworksBuildPhaseObj(myProj.getFirstTarget().uuid).files; for(var i=0; i<pbxFrameworksBuildPhaseObjFiles.length;i++) { var frameworkBuildPhaseFile = pbxFrameworksBuildPhaseObjFiles[i]; if(frameworkBuildPhaseFile.comment && frameworkBuildPhaseFile.comment.indexOf(fileBasename) != -1) { fileId = frameworkBuildPhaseFile.value; pbxFrameworksBuildPhaseObjFiles.splice(i,1); // MUST remove from frameworks build phase or else CodeSignOnCopy won't do anything. break; } } return fileId; } function getFileRefFromName(myProj, fName) { const fileReferences = myProj.hash.project.objects['PBXFileReference']; var fileRef = ''; for(var ref in fileReferences) { if(ref.indexOf('_comment') == -1) { var tmpFileRef = fileReferences[ref]; if(tmpFileRef.name && tmpFileRef.name.indexOf(fName) != -1) { fileRef = ref; break; } } } return fileRef; } const xcodeProjPath = fromDir('platforms/ios','.xcodeproj', false); const projectPath = xcodeProjPath + '/project.pbxproj'; const myProj = xcode.project(projectPath); function addRunpathSearchBuildProperty(proj, build) { const LD_RUNPATH_SEARCH_PATHS = proj.getBuildProperty("LD_RUNPATH_SEARCH_PATHS", build); if(!LD_RUNPATH_SEARCH_PATHS) { proj.addBuildProperty("LD_RUNPATH_SEARCH_PATHS", "\"$(inherited) @executable_path/Frameworks\"", build); } else if(LD_RUNPATH_SEARCH_PATHS.indexOf("@executable_path/Frameworks") == -1) { var newValue = LD_RUNPATH_SEARCH_PATHS.substr(0,LD_RUNPATH_SEARCH_PATHS.length-1); newValue += ' @executable_path/Frameworks\"'; proj.updateBuildProperty("LD_RUNPATH_SEARCH_PATHS", newValue, build); } } myProj.parseSync(); addRunpathSearchBuildProperty(myProj, "Debug"); addRunpathSearchBuildProperty(myProj, "Release"); // unquote (remove trailing ") var projectName = myProj.getFirstTarget().firstTarget.name.substr(1); projectName = projectName.substr(0, projectName.length-1); //Removing the char " at beginning and the end. const groupName = 'Embed Frameworks ' + context.opts.plugin.id; const pluginPathInPlatformIosDir = projectName + '/Plugins/' + context.opts.plugin.id; process.chdir('./platforms/ios'); const frameworkFilesToEmbed = fromDir(pluginPathInPlatformIosDir ,'.framework', false, true); process.chdir('../../'); if(!frameworkFilesToEmbed.length) return; myProj.addBuildPhase(frameworkFilesToEmbed, 'PBXCopyFilesBuildPhase', groupName, myProj.getFirstTarget().uuid, 'frameworks'); for(var frmFileFullPath of frameworkFilesToEmbed) { var justFrameworkFile = path.basename(frmFileFullPath); var fileRef = getFileRefFromName(myProj, justFrameworkFile); var fileId = getFileIdAndRemoveFromFrameworks(myProj, justFrameworkFile); // Adding PBXBuildFile for embedded frameworks var file = { uuid: fileId, basename: justFrameworkFile, settings: { ATTRIBUTES: ["CodeSignOnCopy", "RemoveHeadersOnCopy"] }, fileRef:fileRef, group:groupName }; myProj.addToPbxBuildFileSection(file); // Adding to Frameworks as well (separate PBXBuildFile) var newFrameworkFileEntry = { uuid: myProj.generateUuid(), basename: justFrameworkFile, fileRef:fileRef, group: "Frameworks" }; myProj.addToPbxBuildFileSection(newFrameworkFileEntry); myProj.addToPbxFrameworksBuildPhase(newFrameworkFileEntry); } fs.writeFileSync(projectPath, myProj.writeSync()); console.log('Embedded Frameworks In ' + context.opts.plugin.id); };
What this hook really does:
- Creates a "build phase" named after your plugin ID configured to "Copy files", the purpose of this copy is "Frames".
- Finds and adds your .framework files to the above build phase, in turn embedding it.
- Sets the Xcode build property named
LD_RUNPATH_SEARCH_PATHS to also search for built-in frameworks in "@executable_path/Frameworks" (This was the built-in infrastructure that will be copied after Copy Files → Frames Build Phase - Configures the ATTRIBUTES key by setting "CodeSignOnCopy" and "RemoveHeadersOnCopy" for your .framework files.
- Removes your .framework files from FrameworksBuildPhase and re-adds them to FrameworksBuildPhase as new split PBXBuildFiles (Same PBXFileReference), this should be done so that "CodeSignOnCopy" means anything without deleting it if you open the project using Xcode, You will not find a checkmark in the build phase, which says that it will sign it.
Updated 1: hook code, changes:
- The hook automatically finds your .framework files, there is no need to edit the hook.
- An important modification has been added that sets the ATTRIBUTES "CodeSignOnCopy" and "RemoveHeadersOnCopy" for your .framework files.
- Improved capture so that it works in the case when multiple plug-ins use this hook.
Update 2
- Since my request has been accepted, you no longer need to install my own plug.
- Improved hook code.
Update 3 (09/19/2016)
Changed script hook as suggested by Max Whaler, as I ran into the same issue compared to Xcode 8.
Final note
After you download the application in the AppStore, if the verification fails due to unsupported architectures (i386, etc.), try the following Cordova plugin (hook only, no native code): zcordova-plugin-archtrim