如何在Swift框架中导入私有框架头文件?

29

我有一个Objective-C框架(框架A),其中包含一些公共头文件和私有头文件。这些公共头文件也被声明在框架的Umbrella头文件中。我有一个第二个Swift框架(框架B),它链接到Objective-C框架。

现在,如果我想在B中导入A的公共头文件,我只需要执行import A

但是我该如何导入私有头文件呢?

我知道桥接头文件不是选项,因为它不支持框架。我需要为私有头文件创建一个单独的Umbrella头文件吗?

4个回答

35

你需要修改框架A,使其导出一个私有模块。

  1. A项目中创建一个私有模块映射文件。文件内容应该类似于:

    A/private.modulemap:

    explicit module A.Private {
    
        // Here is the list of your private headers.
        header "Private1.h"
        header "Private2.h"
    
        export *
    }
    
    A框架目标的“构建设置”中,搜索“私有模块映射文件”行,并将其设置为:
  2. $(SRCROOT)/A/private.modulemap
    
  3. 不要将private.modulemap文件包含在“编译源”中。这会导致不必要的警告。

  4. 清理并构建框架A目标。

  5. 在框架B的Swift文件中,你可以像这样导入私有模块:

  6. import A
    import A.Private
    

1
这个方法是可行的,但如果私有头文件中需要导入公共头文件会发生什么呢?在我的情况下,我会收到“找不到头文件”的错误。似乎编译器在编译私有子模块时没有了解公共模块的信息。 - Mihai Damian
1
在我的环境中,它可以正常工作。请查看此项目文件:https://www.dropbox.com/s/srmgbktyhtmtuku/PrivateFWTest.zip?dl=0 - rintaro
结果发现问题是因为我的库位于自己的 Xcode 项目中,该项目被用作外部项目。仅编译库可以解决问题。像您的示例一样将库创建为同一项目中的目标也可以解决问题。 - Mihai Damian
7
我该如何在框架内部使用私有模块? - KoCMoHaBTa
1
即使在模块 A 中,你仍然可以执行 import A.Private - Andrzej Michnia
显示剩余4条评论

8

这个问题发布已经有一段时间了。被接受的回答非常好,就我而言,这是一种常见的方法。

问题在于,它并不真正是“私有的”。你可以在框架内部执行此操作以访问“私有”部分:

// Framework A Swift file
import A.Private

但是,如果你在应用程序中使用框架A(或者将其交付给客户),他仍然可以做到:

// Client App Swift file
import A
import A.Private

// access "private" framework methods and classes

我曾尝试解决这个问题,因为最近我遇到了一个需要将其隐藏不让用户访问的情况(封闭源框架)- 由于这对SDK完整性构成威胁,我不能让任何人访问它。
我找到了解决这个问题的方法,但是过于复杂无法在此完整粘贴。
我在medium上发了一篇文章,也许会帮助到遇到同样问题的人,以下是我的文章链接:

https://medium.com/@amichnia_31596/creating-swift-framework-with-private-objective-c-members-the-good-the-bad-and-the-ugly-4d726386644b


哇!我喜欢你的帖子!最近我遇到了一个关于可选框架和CocoaPods的问题...你能帮我解决吗?https://dev59.com/a7Lma4cB1Zd3GeqPaWcN - Viktor Vostrikov
亲爱的上帝...我已经阅读了它,感谢您的努力。这个解决方案可行,但并不适合我。 - Isaaс Weisberg

5
正如Andrzej Michnia在他的答案中提到的,使用“私有模块映射”解决方案的问题是它并不真正完全私有,那些“私有”的头文件仍然可以被别人看到,因为它们仍然包含在我们的框架中。如果有人使用这样的“私有”模块打开编译后的框架,他仍然会看到你隐藏的所有.h文件。
如果我们需要从其他用户完全隐藏一些目标-C头文件,则另一种可能的方法就是将它们公开,并在手动构建框架或使用bash脚本构建框架后从框架中删除它们。
您可以创建一个单独的头文件,例如“InternalHeaders.h”,在其中导入所有不想公开的头文件。然后在您框架的公共伞头文件中导入此InternalHeaders.h。使所有头文件都是公开的,以便您可以编译所有内容。构建框架后,只需从公共伞头文件中删除“import InternalHeaders.h”,并手动使用bash脚本或在运行时脚本构建阶段中删除所有不想公开的头文件即可。
这仍然不是完美的解决方案,但在某些情况下,它可能比按照其他答案中提出的使用Swift编写协议以匹配每个目标-C接口更容易和更快速。

1

我的情况可能是特定于我的设置,但我将在这里提供它,以防它能帮助其他人。我还有一个Objective-C框架(框架A),其中包含私有标头,我需要在链接它的Swift框架(框架B)中使用它。一些额外的细节:

  1. Each framework is in a separate project in the workspace

  2. The project uses CocoaPods

  3. The podspec defines the following dependence relationship between the two frameworks:

    s.subspec 'FrameworkA' do |cs|
        cs.vendored_frameworks = "lib/FrameworkA.framework"
    end
    
    s.subspec 'FrameworkB' do |ts|
        ts.dependency 'FrameworkA'
        ts.vendored_frameworks = "lib/FrameworkB.framework"
    end
    
@rintaro提供的解决方案在Xcode中运行得很好,但一旦Pod部署后,FrameworkB无法使用位于FrameworkA中的私有模块映射路径找到私有头文件。对我有效的方法是在私有模块映射中使用相对路径到PrivateHeaders目录。
module FrameworkA_Private {
    header "../FrameworkA.framework/PrivateHeaders/Private.h"
    export *
}

这在 Xcode 和使用 CocoaPods 安装的最终产品中都有效。它有点 hacky,因为它引用了最终构建产品中的一个文件夹,并且如果有其他方法告诉 CocoaPods 如何保留这些路径,我不会感到惊讶,但无论如何,目前这个方法解决了问题。

我在使用Pods时也遇到了私有模块映射的问题。据我记得,规格中有module_map设置,但最终我决定采用另一种方法(如果您仍然感兴趣,我在这里发布了答案)。 - Andrzej Michnia

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接