Using a C library inside a Swift framework

A brief tutorial

C is 44 years old. While that may sound like an ancient age for a programming language, C doesn’t care. It’s as energetic and as essential as ever. Yes, it might not be as ‘sexy’ as Swift or Go but it’s still often the best way to perform some low level tasks. Also, it is still the subject of healthy and passionate online debate — this week my Twitter feed was full of opinions and reactions to https://matt.sh/howto-c.

A few days ago I released my latest open source project — a Swift-native framework for zipping and unzipping files called Zip (see GitHub). Zip is built on top of minizip, an open-source C library and integrating the two together was rather challenging.

I’m sure other developers will face similar issues so here’s a quick step-by-step guide on how to use a C library inside a Swift framework.

Step 1 — Preparation

First, copy the C library’s folder to your project’s directory.

The next step is to create a module.modulemap file specifying the name of the framework and the headers you want to expose to your Swift code. For minizip, it looks like this:

module minizip [system][extern_c] {
header “unzip.h”
header “zip.h”
export *
}

Step 2 — Xcode configuration

Add your C library’s folder to your project. Make sure copy items if needed is deselected.

Go to your project’s Build Settings. Find the header Swift Compiler — Search Paths. In Import Paths, add a relative path to your C library’s folder using ${SRCROOT}. If your folder includes subfolders select the recursive option.

Voila! You’re ready to go.

However, there’s one more thing…

Step 3— Podspec configuration (BONUS)

To distribute your Swift framework via Cocoapods, you will need to add the following to your podspec file:

Add the C library source files to s.source_files :

s.source_files = ‘Zip/minizip/*.{c,h}’, ‘Zip/minizip/aes/*.{c,h}’

Add a relative path to the C library to the SWIFT_INCLUDE_PATHS configuration in s.pod_target_xcconfig. It’s imporant to remember that the relative path here is from the podspec’s perspective and it will likely be different to the relative path you added in the build settings.

s.pod_target_xcconfig = {'SWIFT_INCLUDE_PATHS' => '$(SRCROOT)/Zip/Zip/minizip/**'}

Next, you will need to add the module map to the s.preserve_paths:

s.preserve_paths = ‘Zip/minizip/module.modulemap’

Step 4 — Party

Check out the full example on:

github.com/marmelroy/Zip