How to call C code from Swift
Interacting with C libraries from the Swift language is really amazing, from this post can learn the most of C interoperability.
WARN: From Swift 4 there is native support for wrapping C libraries in Swift system module packages. This means that you can easily ship your own system modules, you just have to learn how to use the Swift Package Manager.đ
Bridging header inside Xcode
Letâs fire up Xcode and start a brand new single view app iOS project. Fill out the required fields, and of course choose the Swift language. Next, add a new file and choose the C file template.
After you enter the name and check the also create header file box, Xcode will ask you about the Objective-C bridging header file. Just create it. The name of this file is tricky, because it also supports other C family languages, like pure C or C++, Objective-C and plus-plus. đ
Letâs create a public header for the C code (factorial.h
):
#ifndef factorial_h
#define factorial_h
#include <stdio.h>
long factorial(int n);
#endif /* factorial_h */
This is gona be the implementation of the method (factorial.c
):
#include "factorial.h"
long factorial(int n) {
if (n == 0 || n == 1) return 1;
return n * factorial(n-1);
}
Inside the bridging header, simply import the C header file:
#include "factorial.h"
Somewhere inside a Swift file you can use the factorial method:
print("Hello \(factorial(5))!")
// it actually prints out "Hello 120!" ;)
Compile and run. đš It just works. đ Magic! đ
You can do the exact same thing to use Objective-C classes inside your Swift projects. Apple has great docs about this technique, you should read that if you want to know more about mix and match.
Shipping C code with SPM
The real fun begins when you start using the Swift Package Manager to build C family based sources. From Swift 3.0 you can build C language targets with SPM. If you donât know how to use the SPM tool, you should read my comprehensive tutorial about the Swift Package Manager first.
The only thing that youâll need to do this is a proper directory structure (plus youâll need the package description file), and the package manager will take care all the rest. Here is everything what you need to build the factorial example with SPM.
// swift-tools-version:4.0
import PackageDescription
let package = Package(
name: "cfactorial",
products: [
.library(name: "cfactorial", targets: ["cfactorial"]),
],
targets: [
.target(
name: "cfactorial",
path: "./Sources/factorial"
),
]
)
The directory structure should be something like this.
Sources
factorial
include
factorial.h
factorial.c
You should also change the #include "factorial.h"
line inside the factorial.c
file to #include "include/factorial.h"
because we made a new include directory. This is NOT necessary, but if you donât put your umbrella header into the include directory, youâll need to provide a modulemap
file, and provide the correct location of your header. If you use the include structure SPM will generate everything for you.
With this technique you can import your cfactorial
module from any other Swift package and call the factorial method, like we did through Xcode. You just have to add this module as a dependency, oh by the way you can even call this module from another C project made with SPM! đ„
.package(url: "https://gitlab.com/theswiftdev/cfactorial", .branch("master")),
Congratulations, you just shipped your first C code with Swift Package Manager. This setup also works with C, C++, Objective-C, Objective-C++ code.
Wrapping C [system] modules with SPM
If you want to wrap a C [system] library and call it directly from Swift you can crete a brand new wrapper package with the help of the Swift Package Manager. To start you can use the swift package init --type system-module
command, this will create a generic template project.
These are special packages according to Apple, you just have to ship your own modulemap
and a header file to expose the needed APIs, but first - obviously - youâll need the usual package definition file:
// swift-tools-version:4.0
import PackageDescription
let package = Package(
name: "ccurl",
providers: [
.brew(["curl"]),
.apt(["libcurl4-openssl-dev"])
]
)
Inside the Package.swift file you can set the providers for the library (like brew on macOS or aptitude for Ubuntu / Debian and the others). Here is a good advice for you: sudo apt-get install pkg-config
under Linux to make things work, because the system will search for package header files with the help of the pkgConfig property. For example if you want to use libxml2
and pkg-config
is not installed, you wonât be able to compile / use your system module.
Next youâll need a module.modulemap
file, which is pretty straightforward.
module ccurl [system] {
header "shim.h"
link "curl"
export *
}
About the link property see the Xcode release notes search for âauto-linkingâ
Finally add an extra shim.h header file to import all the required APIs. Usually I donât like to import directly the required header files from the modulemap
file thatâs why I am using this shim.h
- name it like you want - youâll see in a second why am I preferring this method, but here is a basic one.
#ifndef CLIB_SWIFT_CURL
#define CLIB_SWIFT_CURL
#import <curl/curl.h>;
#endif
Letâs talk about why I like importing the shim file. If you have platform differences you can use a neat trick with the help of using macros, for example you can import header files from different locations if you check for the __APPLE__
platform macro.
#ifndef CLIB_SWIFT_EXAMPLE
#define CLIB_SWIFT_EXAMPLE
#ifdef __APPLE__
#include "/usr/local/include/example.h"
#else
#include "/usr/include/example.h"
#endif
#endif
Cool, huh? đ + đš = â€ïž
Related posts
All about the Bool type in Swift
Learn everything about logical types and the Boolean algebra using the Swift programming language and some basic math.
Async HTTP API clients in Swift
Learn how to communicate with API endpoints using the brand new SwiftHttp library, including async / await support.
Beginners guide to functional Swift
The one and only tutorial that you'll ever need to learn higher order functions like: map, flatMap, compactMap, reduce, filter and more.
Beginner's guide to modern generic programming in Swift
Learn the very basics about protocols, existentials, opaque types and how they are related to generic programming in Swift.