在自定义 Cordova 插件中,如何在 plugin.xml 中配置特定的 .framework 文件,以便将其添加到 Xcode 的嵌入式二进制文件"部分?如果目前无法直接在 plugin.xml 中实现,我愿意接受其他建议.
In a custom Cordova plugin, how can I config a specific .framework file in plugin.xml such that it will be added to the "Embedded Binaries" section in Xcode? If that's not currently possible directly in plugin.xml, I'm open to alternative suggestions.
推荐答案我已经实施了一个变通方法,直到 Cordova 的 plugin.xml 支持它,希望在将来,一旦 embed 属性在此类条目中将具有相同的效果:<framework embed="true" src="..."/>,目前,此属性没有帮助,因此有以下解决方法.
I've implemented a workaround until it's supported by Cordova's plugin.xml, hopefully, in the future, once an embed property in such entries will have the same effect: <framework embed="true" src="..." />, for now, this property does not help, hence the following workaround.
以下解决方案使用 Cordova 5.3.3 版.
The following solution worked using Cordova version 5.3.3.
<framework src="pointToYour/File.framework" embed="true" />embed="true" 暂时不起作用,但还是要添加.
embed="true" doesn't work for now, but add it anyway.
<hook type="after_platform_add" src="hooks/embedframework/addEmbedded.js" />接下来,我们需要在钩子的代码中包含一个特定的节点模块,该模块是 节点-xcode.
Next, there's a specific node module we're gonna need in our hook's code, that module is node-xcode.
npm i xcode最后是钩子本身的代码-
Finally, the code for the hook itself -
'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(frameworkBuildPhaseFilement && frameworkBuildPhaseFilement.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); };这个钩子的实际作用:
更新1:钩子代码,修改:
更新 2
更新 3 (19/09/2016)
根据 Max Whaler 的建议修改了钩子脚本,因为我在 Xcode 8 上遇到了同样的问题.
Update 3 (19/09/2016)
Modified hook script according to Max Whaler's suggestion, as I experienced the same issue over Xcode 8.
将应用上传到 AppStore 后,如果由于不支持的架构(i386 等...)导致验证失败,请尝试以下 Cordova 插件(仅挂钩,无本机代码):zcordova-plugin-archtrim
Once you upload your app to the AppStore, if validation fails due to unsupported architectures (i386, etc...), try the following Cordova plugin (only hook, no native code): zcordova-plugin-archtrim
更多推荐
自定义 Cordova 插件:向“嵌入式二进制文件"添加框架
发布评论