Use clang API notes to Annotate C Header Files for Swifty Module Imports

I learned threee new things when I read Doug Gregor’s “Improving the usability of C libraries in Swift” blog post that may also affect your daily life importing C libraries:

  1. The clang compiler support YAML module annotations so you don’t have to own and modify .h files;
  2. The SWIFT_NAME annotation can be used to make free C functions act like methods on objects;
  3. You can tell the compiler to generate object types for automatic reference counting instead of dealing with opaque pointers.

Until today, I was under the impression that you can make C functions sound a little bit nicer and add argument labels with SWIFT_NAME mostly, but this is something on a different level.

One Example to turn a free function into a method:[#20260123webgpu][]

WGPU_EXPORT void wgpuQueueWriteBuffer(
      WGPUQueue queue, WGPUBuffer buffer, uint64_t bufferOffset, void const * data, size_t size) 
  WGPU_FUNCTION_ATTRIBUTE SWIFT_NAME("WGPUQueueImpl.writeBuffer(self:buffer:bufferOffset:data:size:)");

That’s what got me interested: object-oriented conventions from C, expressed in Swift’s notation! Where does the type come from? Usually, you just get opaque pointers.

The WGPUQueueImpl typealias is generated as well with a different annotation – that’s now becoming a reference-counting wrapper around WGPUQueue instead of an opaque pointer. The heavy lifting of this is done by a parameterized annotation, SWIFT_SHARED_REFERENCE(increment, decrement), to which you pass function names to increment and decrement the refcount.

The annotation, if you would apply it manually in the header, would look like this:

// Original typedef is:
//   typedef struct WGPUQueueImpl* WGPUQueue WGPU_OBJECT_ATTRIBUTE;
typedef struct 
  SWIFT_SHARED_REFERENCE(wgpuGroupAddRef, wgpuGroupRelease)
  WGPUQueueImpl* WGPUQueue WGPU_OBJECT_ATTRIBUTE;

… making the generated Swift interface:

// Original generated typealias would've been:
//   public typealias WGPUGroup = OpaquePointer
public class WGPUGroupImpl { }
public typealias WGPUGroup = WGPUGroupImpl

Now the secret ingredient to make this bearable is to not copy the header files, but to use a YAML “sidecar” file to instruct clang to generate a different API. Combining the generated type declaration and the method example from above:

- Name: WGPUGroupImpl
  SwiftImportAs: reference
  SwiftReleaseOp: wgpuGroupRelease
  SwiftRetainOp: wgpuGroupAddRef
- Name: wgpuQueueWriteBuffer
  SwiftName: WGPUQueueImpl.writeBuffer(self:buffer:bufferOffset:data:size:)

clang supports API notes in a YAML file to annotate .h files without having to change the .h files themselves for module imports. That’s so neat: with that you can wrap C libraries in Swift without having to maintain a copy of the headers.

  • API notes are found under the name “Foo.apinotes” for a module named “Foo”.
  • Private module map API notes are picked up as well: “Foo_private.apinotes”
  • Here’s an example file from the clang docs.

clang docs:

Clang will search for API notes files next to module maps only when passed the -fapinotes-modules option.

Next to methods, properties also work with SWIFT_NAME annotations:

- Name: wgpuQuerySetGetCount
  SwiftName: getter:WGPUQuerySetImpl.count(self:)
- Name: wgpuQuerySetGetType
  SwiftName: getter:WGPUQuerySetImpl.type(self:)

Depending on the C API, this can be a game-changer in terms of Swift language ergonomics for you and your team. No more manual refcounting (which, if limited to e.g. a function scope, isn’t that bad thanks to defer, but can get hairy when you need to escape and keep references alive for longer), proper Swift types instead of opaque pointers, and methods and properties on these types instead of namespaced free functions. Lovely.

Auto-Mount Metal Toolchain Using a Launch Service Because Xcode 26 Cannot

Xcode 26 introduced a bug where for some people, me included, Metal shaders won’t compile because the Metal toolchain won’t be managed by Xcode properly, no matter how often you reinstall the toolchain. Some fixes include moving the toolchain into a folder Xcode can see without having to mount anything. Others replace the ExportMetadata.plist exposed version.

I can fix this by mounting the Metal toolchain’s disk image manually. That sucks, but doesn’t break on every update of Xcode or Metal and doesn’t require any fiddling with the files or finding out the expected version string.

So that I don’t forget to do this after rebooting, I wrote a Launch Service that does this after reboot, when /Volumes/ changes, and every 5min for good measure :)

The code: https://codeberg.org/ctietze/metal-toolchain-launchd-automount

If you’re not comfortable fiddling with Launch Services/launchd, the repo includes basic shell scripts to install/uninstall the service for you.

Manually Mount the Metal Toolchain When Xcode Cannot

My Xcode 26.2 still suffers from an inability to compile Metal shader files because it can’t find the Metal toolchain: error: cannot execute tool ‘metal’ due to missing Metal Toolchain; use: xcodebuild -downloadComponent MetalToolchain That command also doesn’t do anything to fix this for me. But I did have success mounting the .dmg file with the toolchain manually.

Continue reading …

Announcing the German Urban Sketchers Index

Teaser image

Happy 2026! Let’s start the year with something fun. The internet shortly became the place of love and joy and sharing that I remember it for: a volunteer who found all (?) Urban Sketching groups in Germany, official and unofficial, reached out to me to send social media links and contact details he found so I include them on the spartan overview page I maintained.

Continue reading …