Swift Libraries and the Pico SDK
22 Sep 2026 ∞
Ever since Embedded Swift was announced I've been trying to use it in my embedded projects, in no small part because I'm not good at writing C. The other part is the same as Swift on the Server: sharing code. Why define the same types in multiple languages in entirely separate codebases when they could be defined once and shared? The easy way to do that is with Swift Package Manager, except for the embedded environment.
When building against the Raspberry Pi Pico SDK you use CMake to build your project and include the SDK, along with any other libraries like USB or networking support. All of the Swift sources need to be included as well, but what about Swift libraries? CMake doesn't know about SPM after all. In previous projects I'd set up a Swift package which was split into a few components. There would be a client library for shared components, an executable for CMake, and a client library for SPM. I also have a separate library with a common protocol for byte stream communication over USB or the network. On the client side I could just import the library, but for the embedded code I'd have to copy (and modify) those files so CMake could use them.
When starting a new project I initially did those same steps, but as I was looking through the third project folder for a file that had colorful printing functions I decided to start looking for other options. There are a few examples for building pure Swift Pico projects, without the SDK, however the hardware support is incomplete. Another project added Swift sources as a library via exposing a C header file, which I didn't want to do, but pointed me in the right direction.
CMake Libraries
After a bunch of fiddling around with various CMake commands I looked at how the Pico SDK exposed libraries. CMake has a standard add_library command, which was exposing a library but not including the sources. The Pico SDK on the other hand has its own pico_add_library command, which for whatever reason did the trick. The sample project built correctly. With that working I moved to my shared protocol library, added a CMakeLists.txt, and for the first time imported an external library provided by SPM. It immediately failed to build, of course. There was a bunch of clean up that needed to be done, mostly decorating various functionality with #if hasFeature(Embedded) to conditionally expose code.
One of my goals was to have all of the source files available in both SPM and CMake so I could edit, and most importantly test, locally. This method allows me to do just that. All the various types and functions are available in both environments, with specific embedded code like GPIO exposed only as needed.
The Package Structure
To expose a target for CMake I'm adding a CMakeLists.txt in the target sources, along with one at the root of the package which exposes the subdirectories.
- MyPackage
- Package.swift
- CMakeLists.txt
- Sources
- MyLibrary
- MyClass.swift
- CMakeLists.txt
- MyLibrary
The file in MyLibrary should look something like this:
pico_add_library(MyLibrary)
target_sources(MyLibrary PUBLIC
MyClass.swift
)
The top-level CMakeLists.txt simply adds any subdirectories you want to expose as libraries:
add_subdirectory(Sources/MyLibrary)
In your Pico project package add the dependency like normal and then in the top-level CMakeLists.txt for the project add the library like so:
add_subdirectory(${CMAKE_CURRENT_LIST_DIR}/.build/checkouts/MyPackage)
target_link_libraries(PicoProject
MyLibrary
pico_stdlib
)
One limitation I've noticed so far is this doesn't seem to allow for nested libraries. For example I initially tried to add some shared project files as a library, but it couldn't see the linked library files. There might be a way to solve this, but CMake seems to only like adding files from subdirectories.
It might also be nice to add a custom function, something like add_spm_package which would simply take the package name and handle adding the subdirectory in the .build folder, just to make things a little easier.