Davide De Rosa

Cross-platform Swift: C interop (pt. 1)

Cross-platform Swift: C interop (pt. 1)

part of a series: < prev | next >

Table of contents (ongoing)

  1. Introduction
  2. Combine
  3. Core libraries
  4. Platform specifics
  5. C interop (pt. 1)
  6. C interop (pt. 2)

This is where the fun begins. Partout, the library I talk about in this series, is a networking library. As such, there are places where code control and performance are paramount. The integration of Swift with the C language for low-level routines is neat, and SwiftPM makes it very easy to mix Swift and C code as if they were the same thing.

The dangers of Apple “Mix and Match”

ObjC is the Tinky Winky hand

I said “mix Swift and C code”, but I should have said “Swift, C, and Objective-C code”. Given that Swift was born off the ribs of Objective-C and the everlasting NeXSTEP library –I know you love that ubiquitous NS prefix–, it still owes a big legacy to that system. Since the beginning, Apple offered great tools to make the migration from ObjC to Swift as seamless as possible. In fact, the process has been so smooth that you may have a hard time spotting the Objective-C entities of a Swift codebase.

While this is amazing for Apple-oriented software with plenty of legacy, this is no less than a disgrace when you port Swift elsewhere, because Objective-C is not available to Swift out of the Apple ecosystem. At least, not easily at all.

Embrace the power of C

The mix-and-match transition to Swift may lead your codebase to a place where it’s very difficult to gauge the extent of the Objective-C legacy. Why is this important? Because if your intent is going cross-platform, you’d better port all your Objective-C code to good old C. The alternative is manually linking against GNUstep, but it’s niche GPL software, and adds unnecessary complexity to keep around a language that is already obsolete.

In my case, the low-level part of my OpenVPN implementation in Partout was written in Objective-C plus OpenSSL, so I decided it was time to patiently write a new C version.

You have no idea how thrilled I am that this new Swift/C implementation now works on Apple, Windows, Linux, and Android!

It took me a couple of weeks to have a successful connection over the new code, and not only was it more pleasant (Objective-C is not praised for its beauty), it was also significantly faster. The image below shows a speed test comparing the performance of my C (1) and ObjC (2) implementations of the OpenVPN protocol over the same 5G connection.

Can I finally mention the joy of using C again after years of high-level programming?

OpenVPN over C versus ObjC

Let countless options unfold

As you dig into C interop, you realize how many things you can do with Swift in both an abstract and performant fashion. WWDC 2024 even introduced the Embedded Swift mode to really go thin and low-level, and C is the natural choice when it comes to communicating with an operating system API, or non-Swift libraries.

At a later stage, we’ll have to face the complications of distributing the Swift runtime, but having C at disposal may dramatically reduce the impact of third-party dependencies, including those Swift itself relies on. Let me anticipate a few examples:

  • The burden of Foundation, or even Concurrency, may be avoided with a tailored C API for our specific domain.
  • The WireGuard backend, that Partout currently integrates via wireguard-go, can communicate with the Linux/Windows kernel module instead.
  • If the footprint is important, the OpenSSL dependency may be replaced with OS APIs, or used in SwiftPM as a shared library. This is common on Linux.

Bottom line

C is a first-class citizen in the Swift ecosystem, and it’s the door to literally everything about a device. Keep enjoying the convenience of the modern Swift abstractions, but don’t fear the power of C when necessary, because C code compiles everywhere with little to no modifications. Some stuff is incredibly easier in C than Swift.

In the next article, I’ll show you how to survive the deceptive complexity of Swift/C bridging.

Keep reading: Cross-platform Swift: C interop (pt. 2)