【问题标题】:Cordova Plugin: Add custom framework using weak linkingCordova 插件:使用弱链接添加自定义框架
【发布时间】:2016-09-04 15:20:48
【问题描述】:

在我的 plugin.xml 中,我试图声明一个自定义的 .framework 并对其进行弱链接,但是一旦我打开 Xcode,我看到添加的框架仍被标记为“必需”而不是“可选”。

这是我的 plugin.xml 条目:

<framework src="ios/libs/BlaBla.framework" custom="true" weak="true" />

这是我收到的第 3 方自定义 .framework,其中包含标题(显然)和共享动态库文件(我将在运行时使用 dlopen("TheDylib", RTLD_LAZY|RTLD_GLOBAL); 加载)。

我不能使用&lt;header-file src="BlaBla.framework/Headers/Bla.h" /&gt; 的原因是.framework 中的标头本身引用带有#import &lt;BlaBla.framework/SomeHeader.h&gt; 的内部标头,因此&lt;header-file&gt; 标记在这种情况下无济于事。

【问题讨论】:

    标签: ios xcode cordova cordova-plugins plugin.xml


    【解决方案1】:

    重要提示

    最好使用“嵌入式框架”功能而不是此解决方案,因为自 iOS 8.0 以来,dlopen 在非 Mac/模拟器设备(真正的 iPhone/iPad)上被禁止。

    看看Custom Cordova Plugin: Add framework to "Embedded Binaries"

    重要提示结束

    我最终做了一些不同的事情,我没有将.framework 声明为&lt;framework ... /&gt; 标签,而是执行了以下操作。

    我创建了一个插件挂钩,将插件目录添加到 FRAMEWORK_SEARCH_PATHS Xcode 构建属性。

    <hook type="after_platform_add" src="hooks/addPluginDirToFrameworkSearchPaths/hook.js" />
    

    挂钩代码:

    module.exports = function(context) {
        const includesiOS = context.opts.platforms.indexOf('ios') != -1;
        if(!includesiOS) return;
    
        const
            deferral = context.requireCordovaModule('q').defer(),
            pluginId =  context.opts.plugin.id;
    
        const xcode = require('xcode'),
            fs = require('fs'),
            path = require('path');
    
        function fromDir(startPath,filter, rec) {
            if (!fs.existsSync(startPath)){
                console.log("no dir ", startPath);
                return;
            }
    
            const files=fs.readdirSync(startPath);
            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) {
                    return filename;
                }
            }
        }
    
        const xcodeProjPath = fromDir('platforms/ios','.xcodeproj', false);
        const projectPath = xcodeProjPath + '/project.pbxproj';
        const myProj = xcode.project(projectPath);
    
        function unquote(str) {
            if (str) return str.replace(/^"(.*)"$/, "$1");
        }
    
        function getProjectName(myProj) {
            var projectName = myProj.getFirstTarget().firstTarget.name;
            projectName = unquote(projectName);
            return projectName;
        }
    
        function set_FRAMEWORK_SEARCH_PATHS(proj) {
            const lineToAdd = '"\\"' + getProjectName(proj) + '/Plugins/' + pluginId + '\\""'
    
            const FRAMEWORK_SEARCH_PATHS =  proj.getBuildProperty("FRAMEWORK_SEARCH_PATHS");
            if(FRAMEWORK_SEARCH_PATHS != null) {
                const isArray = typeof FRAMEWORK_SEARCH_PATHS != 'string';
                if(isArray) {
                    for(var entry of FRAMEWORK_SEARCH_PATHS) {
                        if(entry.indexOf(pluginId) != -1) {
                            return false; // already exists, no need to do anything.
                        }
                    }
                } else { // string
                    if(FRAMEWORK_SEARCH_PATHS.indexOf(pluginId) != -1) {
                        return false; // already exists, no need to do anything.
                    }
                }
    
                var newValueArray = isArray?FRAMEWORK_SEARCH_PATHS:[FRAMEWORK_SEARCH_PATHS];
                newValueArray.push(lineToAdd);
    
                proj.updateBuildProperty("FRAMEWORK_SEARCH_PATHS", newValueArray);
            } else {
                proj.addBuildProperty("FRAMEWORK_SEARCH_PATHS", lineToAdd);
            }
            return true;
        }
    
        myProj.parse(function (err) {
            if(err) {
                deferral.reject('Error while parsing project');
            }
    
            if(set_FRAMEWORK_SEARCH_PATHS(myProj)) {
                fs.writeFileSync(projectPath, myProj.writeSync());
                console.log('Added Framework Search Path for ' + pluginId);
            } else {
                console.log('Framework Search Path was already added for ' + pluginId);
            }
    
            deferral.resolve();
        });
    
        return deferral.promise;
    };
    

    注意:hook 依赖于一个名为“xcode”的 NPM 依赖项,npm i xcode --save 之前也是如此(无需编辑 hook 代码)。 现在我们在plugin.xml中声明将.framework内容导入我们的项目的方式如下:

    <source-file src="ios/libs/CameraWizard.framework" />
    <resource-file src="ios/libs/CameraWizard.framework/CameraWizard" />
    

    我们使用source-file 标签只是为了导入.framework,因为我们只希望它被复制到iOS平台插件目录,我们不希望它“强”链接,我们需要它的原因只是因为它是Headers,而不是二进制。我们的钩子会为其添加正确的框架搜索路径。

    然后我们使用resource-file只导入.framework目录内的共享库文件,我们将其添加为资源,这样当应用启动时调用dlopen(...),共享库将在运行时找到.

    最后,现在要在插件代码中使用共享库,请执行以下操作:

    1. #import &lt;dlfcn.h&gt;(同时导入您的 .framework 的标头)。
    2. -(void)pluginInitialize方法下,加载共享库:

      NSString* resourcePath = [[NSBundle mainBundle] resourcePath]; NSString* dlPath = [NSString stringWithFormat:@"%@/FrameworkFileNameInResourceTag", resourcePath]; const char* cdlpath = [dlPath UTF8String]; dlopen(cdlpath, RTLD_LAZY|RTLD_GLOBAL);

    3. 现在使用共享库中的类:

      SomeClassInFramework someInstance = [(SomeClassInFramework)[NSClassFromString(@"SomeClassInFramework") alloc] init];

    【讨论】:

      猜你喜欢
      • 2018-10-19
      • 2018-06-20
      • 1970-01-01
      • 2011-02-07
      • 2023-03-11
      • 1970-01-01
      • 1970-01-01
      • 2014-10-19
      • 2013-01-06
      相关资源
      最近更新 更多