Code written in C can be used inside swift directly by using clang’s module maps. Module map allows to define C code as a module and import it into swift code using import statements. Swift package manager has a documentation on how to use and link system libraries using SPM.
How it works?
C shared libraries (.so or .dylib) should be present at correct positions (for eg /usr/local/lib on OSX and /usr/lib on ubuntu)
A swift package should be created containing a module.modulemap file
module.modulemap file defines the name of the package which will be used in swift code, header file which will be exposed, which c library has to be linked, etc
SPM recommends prepending C in name of the library when making its swift package. for eg if a c library is called Foo, its swift name should be CFoo
A small example
In this example we’ll create a C Library to compute factorial of the number supplied and display its result using swift.
Create Factorial.h and Factorial.c
$ mkdir CGetFactorial && cd CGetFactorial && mkdir CSources && touch CSources/Factorial.h && touch CSources/Factorial.c
- Create Package.swift, module.modulemap and makefile
Package.swift This contains name and dependencies of our package. This can be left empty but the file has to be present.
module.modulemap This file will help clang to define the c library as module for swift.
the module name used here will be the name of the package that can be imported into swift
makefile We will use make to install our c library into the system
This a simple makefile which will compile our Factorial.c into a shared library and copy it into /usr/lib for ubuntu and /usr/local/lib for OSX.
And we’re done with our C package ready to be used with swift.
Lets create a swift package called SwiftyFactorial
mkdir SwiftyFactorial && cd SwiftyFactorial && touch Package.swift && touch main.swift
Package.swift define the dependency to the C Package we created above
main.swift import the C Package and use the method we wrote.
Okay! Lets try building this package.
$ swift build Cloning Packages/CGetFactorial Using version 1.0.0 of package CGetFactorial Compiling Swift Module 'SwiftyFactorial' (1 sources) Linking Executable: .build/debug/SwiftyFactorial ld: library not found for -lFactorial for architecture x86_64 <unknown>:0: error: link command failed with exit code 1 (use -v to see invocation) <unknown>:0: error: build had 1 command failures
This failed because the C factorial library is not yet installed in our system. Go to the cloned package
$ cd Packages/CGetFactorial-1.0.0/
run make to install it. (You’ll need to run
sudo make for ubuntu)
$ make clang -shared CSources/Factorial.c -o libFactorial.so cp libFactorial.so /usr/local/lib
That should do it. Go back and try to run swift build again.
$ cd ../../ $ swift build Compiling Swift Module 'SwiftyFactorial' (1 sources) Linking Executable: .build/debug/SwiftyFactorial
Great, try to run the executable
$ .build/debug/SwiftyFactorial factorial of 20 = 2432902008176640000
Done. Swift is correctly importing the
CGetFactorial package and is able to call the method.
To remove the installed library from your system just run the
make clean inside the CGetFactorial package dir.
$ cd Packages/CGetFactorial-1.0.0 && make clean rm -f libFactorial.so rm -f /usr/local/lib/libFactorial.so
It is fairly simple to import and use C inside swift code but SPM can’t yet build C code for you which will simplify this process if we need to ship C code which has to be linked to our swift packages.
The above code can be downloaded from github: https://github.com/aciidb0mb3r/SwiftyFactorial