[iOS翻译]如何将C库集成到用Swift编写的iOS应用中去

929 阅读5分钟

原文地址:medium.com/@distillery…

原文作者:medium.com/@distillery…

发布时间:2018年9月19日-5分钟阅读

image.png

如今,每个人都重视隐私和安全。这就是为什么最近,当我们需要在Distillery的一个项目中使用一个加密库时,这并不奇怪。

在这个项目中,我们决定使用libsignal。libsignal最初是为Signal Private Messenger开发的,在安全专家中有着良好的声誉。

使用libsignal

2018年7月底,在Pure C、Java和JavaScript中都有libsignal的实现。在Objective-C中也有一个名为SignalProtocolKit的实现,但当时它已经被废弃了。

我们的目标是在一个用Swift编写的iOS应用中使用这个库。为了让所有的东西都能跟上时代的步伐,我们不得不使用用C语言编写的,其源代码就存放在这里。幸运的是,Swift可以非常顺利地与C代码进行交互,至少是因为苹果的一些低级库是用C编写的。 棘手的部分是将库正确地添加到项目中。

创建一个模块.map

经过一番研究,我发现我可以简单地将库的源代码添加到项目中,并创建一个module.map文件来指定我想要暴露库的哪些头文件(更多细节,请查看这篇博客)。 libsignal包含了很多头文件,但如果我想在普通的C项目中使用它,我只需要包含一个叫做signal_protocol.h的头文件。因此,我做了一个模块.map文件,内容如下。

module SignalProtocol [system] {
headersrc/signal_protocol.h”
export *
}

然后,我把它放到有库源的文件夹中,添加到项目中,并开始测试。

Xcode允许导入这个模块并使用一些库函数,但有些库组件缺失。例如,我无法使用signal_protocol_internal.h中定义的signal_context数据类型。

那一刻,很明显,Xcode并没有正确地包含嵌套的头文件。所以我决定尝试在我的module.map文件中包含libsignal的所有头文件。(把更新后的module.map放在这里是多余的,因为它只是包含了每个*.h文件,类似于上面的文件)。

在我做完这些并尝试构建来构建项目之后,我得到了很多与libsignal的一些头文件有关的错误。当我查看它们时,我意识到有一些头文件不是真正的头文件。相反,它们存储了一些常量值,使用方法如下。

static const fe sqrtm1 = {
#include “sqrtm1.h”
} ;

截至2018年7月底,以下文件是这样使用的。

sqrtm1.h
ge_sub.h
pow225521.h
ge_add.h
d2.h
ge_madd.h
ge_msub.h
pow22523.h
ge_p2_dbl.h
d.h
base2.h
base.h

最后,在将这些文件从我的module.map中排除后,我就可以建立项目并使用libsignal的所有部分了。

这是一个胜利吗?我想,算是中等程度的胜利吧。将第三方库的源码直接添加到项目中似乎不太合适,所以我们决定将libsignal封装在Cocoa Touch框架中。

在Cocoa Touch框架中封装它。

起初,在经历了之前的种种困难后,我以为这部分会很简单。不幸的是,事实证明我错了,就在我打开我们刚创建的项目框架的Build Settings的那一刻,找不到 "Swift Compiler - Search Paths "部分。

接下来是几次尝试在框架中加入libsignal源码。我只介绍成功的那一次。

要在Cocoa Touch框架中封装一个С库(以libsignal为例),请按照以下步骤进行。

1. 创建一个新项目。

创建一个新项目。对于其类型,选择 "Cocoa Touch Framework"。

2. 添加源文件。

将库的源文件添加到项目中。

3. 选择 "要暴露的头文件"。

进入项目文件,选择你的目标框架。打开 "构建阶段 "选项卡。在这里,在 "Headers "部分,应该会出现库中所有的头文件。通过拖动它们到 "Public "列表来决定要暴露哪些头文件。在libsignal的例子中,我决定将所有的头文件都公开。

顺便说一下,这里你应该有所有的*.h文件,甚至那些只是存储常量的文件。

image.png

4. 导入头文件。

现在,既然所有的头文件都已经添加到你的项目中,你必须将它们导入到框架的主头文件中,它应该叫做[你的框架名称].h,并放在框架的根目录下。为每一个实际的头文件添加以下一行,省略那些只存储常量的文件。

#import "protocol.h"

请注意,由于我们把所有的头文件都添加到了项目文件中,我们只需要使用filename,而不需要在项目中指定文件的整个路径。这样做的原因是,所有的头文件都被复制到框架捆绑中,而项目中的所有路径都是无效的。

image.png

Voilà! 你应该有一个功能齐全的框架。

但请注意,目前它有一个主要的缺点:它保留了一个特定版本的 libsignal,不允许你从 GitHubrepository 更新它。不过,这个问题不在本文的讨论范围之内。

想了解更多关于Distillery的开发人员如何创造创新的技术解决方案来推动我们的项目吗?请查看我们的其他博客--比如这篇关于优化UIScrollView的博客,或者这篇关于使用Travis进行持续集成的博客--或者让我们知道

关于作者

iOS开发者Artem Garmash于2017年加入Distillery。在空闲时间,他喜欢做硬件项目,要么修改现有设备,要么从头开始打造新硬件。他还喜欢骑山地车,在雪地里散步,听很多音乐。


通过www.DeepL.com/Translator(免费版)翻译