Objective-C 和 Swift 框架内的互操作性和模块映射

Objective-C and Swift interoperability inside framework with module mapping

上下文

几年前,我的公司构建了一个使用 C 库的 Objective-C 框架,现在我们正试图将其转换为 Swift。由于我们需要使用这个 C++ 库,而不是从基础中完全消除 objective-c 代码,我们需要保持这两种语言之间的互操作性。 关键是我们需要以一种并非所有接口都可用于使用框架的应用程序的方式来实现它——这就是我的问题所在。

尝试次数

我搜索的第一件事是关于如何将 C++ 库与 Swift 一起使用,根据我搜索的所有内容,我们需要使用 Objective C 在 C++ 和 Swift 之间创建一座桥梁。所以我开始专注于尝试创建 swift 和 objective c 之间的互操作性,而 C++ 库自然来自 C++ 和 Objective-C.

之间的现有连接

经过一千次 google 搜索和 safari 选项卡后,我发现在 app 中,我们需要做的就是创建一个桥接 header 和我们很高兴,既不简单也不困难,但有点麻烦,但在框架内我们没有桥接 headers,唯一可以实现的方法是 ModuleMaps,这些人就像桥接一样headers 将 ObjC 框架封装在模块中。

所以我尝试了这个并且成功了!万岁!

但问题是:为了完成这项工作,我需要制作我的 headers public(以及我的新 swift 类 public)。因此进行了更多搜索,我发现 Private module maps。 我试图从这样的教程中重新创建一个:

framework module MyDeprecatedObjCFramework {
    umbrella header "MyDeprecatedObjCFramework.h"

    export *

    explicit module Private {
         header "Path/To/Private/Header.h"
    }
}

但是编译器并没有考虑模块名称结尾处的 Private 关键字,我能够从框架外部访问它。为其设置名称也不起作用(例如 MyDeprecatedObjCFrameworkPrivateMyDeprecatedObjCFramework_PrivateMyDeprecatedObjCFramework-Private,根据 clang 文档,当前模式是 _Private)。

单独创建一个文件并将其设置为构建设置中的 Private Module Map 对我来说也不起作用,在这种情况下有两次尝试,第一个是这样的:

module MyDeprecatedObjCFramework_Private {
    header "Path/To/Private/Header.h"
    export *
}

在这种情况下,任何人都无法访问该模块,即使在框架内部也是如此。简而言之,它没有被复制到 Products Xcode 文件夹中的输出框架。在 module 之前添加关键字 framework 导致 include of non-modular header inside framework module.

我还发现这些 clang 东西不够稳定,可能会在主要版本之间中断。从苹果本身来看,viable workaround and the only solution for this guy 似乎并不是创建一个 lint 来防止外部应用程序导入私有 headers/swift 类,这在我看来有点混乱。

最后一题

是否可以在框架内创建 objective-c 和 swift 之间的互操作性?这可行吗?从头开始重新创建整个框架会更好吗?或者拆分到其他swift框架中比较好?

我遇到过类似的问题,但没有找到理想的解决方案。据我所知,您列出了所有替代方案。 Apple documentation 很清楚:

To use the Objective-C declarations in files in the same framework target as your Swift code, you’ll need to import those files into the Objective-C umbrella header—the master header for your framework. Import your Objective-C files by configuring the umbrella header:

正如您所说,甚至 Apple 也不鼓励您使用模块映射,并建议将您的 Objective-C 代码放在一个单独的目标中。

如果您有时间,从头开始重新创建整个框架可能是最好的解决方案。