Doug Gregor is a member of the Language Steering Group and works on the Swift compiler and runtime.
There are many interesting, useful, and fun C libraries in the software ecosystem. While one could go and rewrite these libraries in Swift, usually there is no need, because Swift provides direct interoperability with C. With a little setup, you can directly use existing C libraries from your Swift code.
When you use a C library directly from Swift, it will look and feel similar to using it from C. That can be useful if you’re following sample code or a tutorial written in C, but it can also feel out of place. For example, here’s a small amount of code using a C API:
var instanceDescriptor = WGPUInstanceDescriptor () let instance = wgpuCreateInstance ( & instanceDescriptor ) var surfaceDescriptor = WGPUSurfaceDescriptor () let surface = wgpuInstanceCreateSurface ( instance , & surfaceDescriptor ) if wgpuSurfacePresent ( & surface ) == WGPUStatus_Error { // report error } wgpuSurfaceRelease ( surface ) wgpuInstanceRelease ( instance )
The C library here that Swift is using comes from the webgpu-headers project, which vends a C header ( webgpu.h ) that is used by several implementations of WebGPU. WebGPU is a technology that enables web developers to use the system’s GPU (Graphics Processing Unit) from the browser. For the purposes of this post, you don’t really need to know anything about WebGPU: I’m using it as an example of a typical C library, and the techniques described in this blog post apply to lots of other well-designed C libraries.
The Swift code above has a very “C” feel to it. It has global function calls with prefixed names like wgpuInstanceCreateSurface and global integer constants like WGPUStatus_Error . It pervasively uses unsafe pointers, some of which are managed with explicit reference counting, where the user provides calls to wpuXYZAddRef and wgpuXYZRelease functions. It works, but it doesn’t feel like Swift, and inherits various safety problems of C.
Fortunately, we can improve this situation, providing a safer and more ergonomic interface to WebGPU from Swift that feels like it belongs in Swift. More importantly, we can do so without changing the WebGPU implementation: Swift provides a suite of annotations that you can apply to C headers to improve the way in which the C APIs are expressed in Swift. These annotations describe common conventions in C that match up with Swift constructs, projecting a more Swift-friendly interface on top of the C code.
In this post, I’m going to use these annotations to improve how Swift interacts with the WebGPU C code. By the end, we’ll be able to take advantage of Swift features like argument labels, methods, enums, and automatic reference counting, like this:
var instanceDescriptor = WGPUInstanceDescriptor () let instance = WGPUInstance ( descriptor : & instanceDescriptor ) var surfaceDescriptor = WGPUSurfaceDescriptor () let surface = instance . createSurface ( descriptor : & surfaceDescriptor ) if surface . present () == . error { // report error } // Swift automatically deallocates the instance and surface when we're done
These same annotations can be used for any C library to provide a safer, more ergonomic development experience in Swift without changing the C library at all.
... continue reading