October 13, 2023·5 min read·engineeringswiftwindowsinteroperabilitycom

Swift's Windows interoperability depends on imports and object layout

Swift's Windows interoperability article explains Clang modules and COM bridging, including why macros alone cannot provide native COM object layout.

Calling an operating-system function and implementing an object that the operating system can call are different interoperability problems. The first needs an imported declaration with the correct calling convention. The second can require a specific representation in memory, including the layout through which foreign code locates methods.

Saleem Abdulrasool's October 13 Swift interoperability article explains that distinction through Windows APIs and COM. Originally published by The Browser Company, it describes existing bridges and possible language improvements. Its central contribution is an account of mechanisms and limitations, rather than a report that one new language annotation has completed Windows integration.

I find Swift's design appealing when its safety model can coexist with an existing native API. Windows makes the boundary concrete. Importing a declaration is useful, but object lifetime and layout still need an explanation before a foreign object can behave predictably in Swift.

Clang modules expose the C APIs

The article explains that Swift embeds Clang to import C and C++ declarations. On Windows, the C portion of the SDK becomes accessible through a Clang module called WinSDK. A Swift overlay refines some of those definitions into forms more convenient for Swift callers.

A Clang header module uses a module.modulemap file to describe a collection of declarations and its dependencies. That supplies the compiler with a defined library boundary. The mechanism makes existing C declarations available without requiring a manually rewritten Swift declaration for every function.

The article uses this to explain why Swift can build Windows command-line and GUI applications even while support for newer API families remains incomplete. A substantial portion of the Windows API is exposed through C. Other portions require additional interoperability work.

That distinction prevents a misleading reading of platform support. A working compiler and access to WinSDK do not establish that every modern Windows API already has an equally direct Swift interface. The shape of each API and the supported interoperation mechanisms determine the boundary.

C++ broadens the available library choices

Swift 5.9 adds bidirectional interoperability with C++ for certain API shapes. The release announcement describes that support as actively evolving, with some aspects subject to change as developers use it in mixed-language codebases.

The Windows article gives Firebase as a motivating example. Its Swift SDK is described as Apple-platform-specific and based on Objective-C, while a cross-platform C++ SDK is also available. The article points to swift-firebase as a bridge being developed to expose that C++ SDK to Swift clients.

This is an example of access to an existing implementation changing what can be built. A library author does not have to reproduce an entire service client before Swift can use it on another platform. A suitable foreign API and bridge can provide an incremental path.

The example should retain its status as work under development. It does not prove that every SDK feature is covered or that every application can adopt the bridge without constraints. The source uses it to show the opportunity created by C++ interoperability.

COM adds runtime interface discovery

COM objects expose functionality through interfaces identified by globally unique interface identifiers. The article explains that these interfaces inherit from IUnknown, whose operations include reference counting and querying for another supported interface.

AddRef and Release manage ownership of references. QueryInterface asks whether the object supports a particular interface and obtains access to it. This differs from importing a single ordinary C function, since the caller discovers the relevant interface dynamically.

The article draws a semantic comparison with class-bound Swift types. Swift classes use automatic reference counting, while COM exposes explicit reference-counting operations. Both models can represent references to an object with identity, but the foreign calling and lifetime rules still have to be respected.

The comparison helps explain a wrapper's purpose. A Swift wrapper can hide repeated pointer indirection and present methods in a more convenient form. It cannot ignore the representation through which the underlying COM interface receives calls.

The C representation provides a bridge

COM specifies an ABI. That includes how calls are arranged and how parameters cross the boundary. Its interface can be expressed in C through a pointer to a table of function pointers, allowing Swift's C interoperability to provide a route to those operations.

The article points to Swift/COM as code demonstrating that bridge. It also describes DXSample, which uses bridged interfaces for a DirectX example with a three-dimensional cube and shaders. These are concrete demonstrations of access through existing mechanisms.

A wrapper around a raw interface pointer reduces repeated low-level code. The article's DirectX example implements a Swift method that locates the foreign method through the interface's table, calls it with the appropriate receiver pointer, and checks the resulting status.

That work is mechanical, but its correctness matters. The pointer, method entry, and lifetime policy refer to an object implemented outside Swift's ordinary class model. Presenting a Swift method name does not itself establish that the underlying object has Swift's native representation.

Why a macro cannot determine the ABI

The article discusses a possible COM annotation that could make Swift implementations more convenient. It presents that spelling as an idea requiring a proper evolution proposal. It is not an available annotation introduced by the article.

Macros could generate some wrapper boilerplate. They cannot alone control the native object layout needed to make an ordinary Swift object satisfy COM's ABI. The compiler would need to participate in that representation decision.

This is the most useful design boundary in the article for me. Source generation can create declarations and forwarding calls, but an external ABI imposes requirements below that level. Treating those as separate responsibilities keeps an attractive annotation from concealing an unsolved representation problem.

The article also describes reference-counted foreign-type support through SWIFT_SHARED_REFERENCE and explains how COM's lifetime methods can fit that model. More direct virtual-method support is still under development in its account. Those mechanisms offer a path toward less manual bridging, while the current C-based route remains necessary for the limitations described.

Swift's Windows story therefore includes several distinct forms of integration. WinSDK imports expose C APIs, evolving C++ support increases access to existing libraries, and COM wrappers handle runtime-discovered interfaces with an explicit ABI. The proposed language-level COM work would need to connect object layout with those existing mechanisms before its example annotation could become a usable feature.