How to Integrate a C Library into an iOS App Written in Swift

Voiced by Amazon Polly

Nowadays, everyone values privacy and security. That’s why it wasn’t surprising when, recently, we needed to use an encryption library on one of our Distillery projects.

For the project in question, the decision was made to use libsignal. Originally developed for Signal Private Messenger, libsignal has a good reputation among security specialists.

Using libsignal

At the end of July 2018, there were implementations of libsignal in Pure C, Java, and JavaScript. There was also an implementation in Objective-C called SignalProtocolKit, but at that time it had already been deprecated.

Our goal was to use the library in an iOS app written in Swift. To keep everything up to date, we had to use the one written in C, for which the source code is stored here. Lucky for us, Swift can interact with C code very smoothly, at minimum because some of Apple’s low-level libraries are written in C. The tricky part was to add the library to the project correctly.

Creating a module.map

After a bit of research, I figured out that I could simply add the library’s source code to the project and create a module.map file to specify which headers of the library I wanted to expose (for more detail, check out this blog). libsignal contains lots of header files, but if I want to use it in a regular C project, I only have to include the one called signal_protocol.h. Since signal_protocol.h includes the other headers it depends on, just as every other header file does, we can be sure that all the files are included. Accordingly, I made a module.map file with the following contents:

module SignalProtocol [system] {
header “src/signal_protocol.h”
export *
}

Then, I put it into the folder with the library sources, added it to the project, and started to test.

Xcode allowed to import this module and to use some of the library functions, but some of the library components were missing. For example, I was unable to use the signal_context data type which is defined in signal_protocol_internal.h.

At that moment it became obvious that Xcode doesn’t properly include nested headers. So I decided to try including all the headers from libsignal in my module.map file. (It’s redundant to put the updated module.map here, as it just includes every *.h file similar to the file above.)

After I did this and tried to build to build the project, I got a lot of errors related to some of the header files from libsignal. When I took a look at them, I realized that there were some header files that were not actual headers. Instead, they store some constant values and are used as follows:

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

As of the end of July 2018, the following files were used that way:

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

Finally, after excluding these files from my module.map, I was able to build the project and use all the parts of libsignal.

Was it a victory? A moderate one, I guess. Adding the sources of a third-party library directly to the project didn’t seem quite right, so we decided to wrap libsignal in the Cocoa Touch Framework.

Wrapping It in the Cocoa Touch Framework

At first, after all the previous difficulties, I thought this part would be easy. Unfortunately, I was proven wrong the exact moment I opened the Build Settings of our freshly created project framework and couldn’t find the “Swift Compiler — Search Paths” section.

What followed were several attempts to include the libsignal sources in the framework. I will only describe the one that was successful.

To wrap a С library in the Cocoa Touch Framework (using the example of libsignal), follow these steps:

1. Create a New Project.

Create a new project. For its type, choose “Cocoa Touch Framework.

2. Add Source Files.

Add the source files of the library to the project.

3. Select Headers to Expose.

Go to the project file and select your target framework. Open the “Build Phases” tab. Here, in the “Headers” section, all the headers from the library should appear. Decide which ones to expose by dragging them to the “Public” list. In the case of libsignal, I decided to make all the headers public.

By the way, here you should have all the *.h files, even those that just store constants.

4. Import Headers.

Now, since all the headers have been added to your project, you have to import them into the main header of the framework, which should be called [your framework name].h and placed in the root directory of the framework. Add the following line for every actual header, omitting those that just store constants:

#import “protocol.h”

Note that, since we added all headers to the project file, we only have to use filename without specifying the whole path to the file in the project. It works this way because all of the headers are copied inside the framework bundle, and all paths inside the project become invalid.

Voilà! You should have a fully functioning framework.

Do note, however, that at the moment, it has one major disadvantage: It keeps a particular version of libsignal and doesn’t allow you to update it from its GitHub repository. That issue, however, is outside the scope of this article.

Want to hear more about how Distillery’s developers create innovative technology solutions that move our projects forward? Check out some of our other blogs — like this one on optimizing UIScrollView or this one on using Travis for continuous integration — or let us know!
 
 

previous post next post
BACK TO TOP >