I’m still enjoying playing with Swift and am beginning to quite like the language. At the moment, I’ve only ever written with it on Linux, so I’m sure I’m making my life harder than if I was using OS X where it’s more mature. On the flip side, if I ever use Swift professionally, it’s most likely going to be on a Linux server as a microservice or a command line app at the other end of a queue.

One thing I’ve been trying to wrap my head around is getting access to a C library from within Swift and I picked libcurl as the one to try.

Getting this going with the Swift Package Manager isn’t that hard once you understand what you’re doing and understand its current limitations!

Firstly, install the the dev package for lib curl so that curl.h and libcurl.so are available:

sudo apt-get install libcurl4-openssl-dev 1 sudo apt-get install libcurl4-openssl-dev

Creating the Swift package

The Swift package manager requires all packages to have a Package.swift file. In this case, it can be empty as we’re not compiling any Swift code.

$ mkdir CCurl && cd CCurl $ touch Package.swift 1 2 $ mkdir CCurl && cd CCurl $ touch Package.swift

(Note the convention for wrapping a C library is to start the name with a capital C and then using camelCase for the rest of the name.)

To bring in the C library, we need a module.modulemap file:

module CCurl [system] { header "/usr/include/curl/curl.h" link "curl" export * } 1 2 3 4 5 module CCurl [system] { header "/usr/include/curl/curl.h" link "curl" export * }

With this file, we tell Swift where the curl.h header file is and that any functions in it are to be found in the lubcurl.so library.

We can now use curl C functions in a test app by pulling in our CCurl package as a dependency to our app.

However, as the Swift package manager only (currently) works with git repositories, we first need to create a git repo for CCurl and tag it:

$ git init $ git add Package.swift module.modulemap $ git commit -m "Initial commit" $ git tag 0.0.1 1 2 3 4 $ git init $ git add Package.swift module.modulemap $ git commit -m "Initial commit" $ git tag 0.0.1

Using a package within an app

We’ll put our app at the same level as our library, so we change directory up a level out of the CCurl one and then make a new directory called app :

$ cd .. $ mkdir app && cd app 1 2 $ cd .. $ mkdir app && cd app

As with our library, our app needs a Package.Swift , but this time we use it to list our dependency:

import PackageDescription let package = Package( dependencies: [ .Package(url: "../CCurl", versions: Version(0,0,1)..<Version(1,0,0)) ] ) 1 2 3 4 5 6 7 import PackageDescription let package = Package( dependencies: [ .Package(url: "../CCurl", versions: Version(0,0,1).. < Version(1,0,0)) ] )

The Package call takes the url to the git repository and the version criteria. Usually you’d have a git URL from GitHub or somewhere, but as we’re doing this locally, we use a relative file-based one. The versions parameter enables us to put in a range of versions. In this case, I’ve said that I’ll accept anything from 0.0.1 up to but not including 1.0.0.

For the Swift package manager to build an application, you must have a main.swift source file. You can have others, but we only need one file, so main.swift it is:

import CCurl let handle = curl_easy_init() print("handle = \(handle)") 1 2 3 4 5 import CCurl let handle = curl_easy_init() print("handle = \ ( handle ) ")

Build the app using swift build and it will grab the CCurl dependency and place it in a Packages directory, build it and then build main.swift for you. The executable is placed in the .build/debug directory and in this case is simply called app

This is just a test that it works and the output on my system is:

$ .build/debug/app handle = 0x0000000001adcbb0 1 2 $ .build/debug/app handle = 0x0000000001adcbb0

Conclusion

That’s all that’s needed to wrap a C library and use it in Swift.