0

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

这是我的 plugin.xml 条目:

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

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

我不能使用的原因<header-file src="BlaBla.framework/Headers/Bla.h" />是它们.framework本身的标题是指内部标题,#import <BlaBla.framework/SomeHeader.h>因此<header-file>在这种情况下标签无济于事。

4

1 回答 1

0

重要的提示

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

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

重要提示结束

我最终做了一些不同的事情,而不是声明.framework<framework ... />标签,我做了以下事情。

我创建了一个插件挂钩,将插件目录添加到 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;
};

注意:钩子依赖于一个名为“xcode”的 NPM 依赖项,npm i xcode --save之前也这样做(无需编辑钩子代码)。现在我们在 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 <dlfcn.h>(也导入你.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];

于 2016-05-10T19:38:01.857 回答