Showing how to use Swift Embedded with Swift Package Manager, Also U8g2 Swift Package on ESP32

Using Swift for Embedded Development on ESP32C6: A 3D Cube Animation Demo

Hello everyone! I'd like to share my experience of using Swift for embedded development on ESP32C6. This project demonstrates how to implement a 3D cube animation using Swift and display it on an OLED screen.

Repo

https://github.com/CmST0us/swift-embedded-xiao-esp32c6-expansion_board

Project Highlights :sparkles:

  • Swift embedded programming on ESP32C6

  • OLED display support using U8g2

  • 3D graphics rendering

  • I2C communication

  • Swift Package Manager integration

  • Using Swift Package Manager Trait

Known Issues

SourceKit-LSP Support

Currently, SourceKit-LSP doesn't work properly in the embedded Swift environment. This affects:

  • Code completion
  • Symbol navigation
  • Error reporting

Maybe I need some help. :grinning_face:

8 Likes

Very nice! I like the approach you took to integrate SwiftPM into the IDF CMake build. I think you could simplify it a bit by placing the compiler flags into a toolset.json file and passing that in. Would help if you have multiple projects that you want to share the same settings. Though placing it in the CMake file produces similar results.

But I really like the trick to get the include path out of the target property. That was the biggest hurdle I've been trying to figure out.

Thank you for your kind words! Your suggestion about using toolset.json is very valuable, and I'll definitely look into that configuration approach. Indeed, centralizing compiler flags in toolset.json would make the configuration cleaner and more reusable, especially for sharing settings across multiple projects. I'll seriously consider this optimization suggestion.

Impressive work!

The SourceKit-LSP support not working has an immediate explanation -- your CMake setup (swift-embedded-xiao-esp32c6-expansion_board/main/CMakeLists.txt at main · CmST0us/swift-embedded-xiao-esp32c6-expansion_board · GitHub) is calling swift build as a "custom command", which means there's no source-to-output mapping for the Swift code in compile_commands.json. For two reasons actually: (1) it's a CMake custom command, and (2) it's not actually constructing the Swift compilation commands in CMake.

I'm not sure what the best solution for that is there (assuming you want to keep using both CMake and SwiftPM). Maybe @bnbarham, @ahoppen, or @etcwilde does?

yes, in my develop workflow, i use vscode to open the idf project. So, yes, i use both cmake and SPM

May be there is some way, SPM (swift build) can export build command?

Unfortunately not today.

This is really cool, thanks for sharing. I'll definitely learn a trick or two from this project.

Just to mention that I had an issue building it and needed a change.

Failure was

[10/15] Generating /Users/…/swift-embedded-xiao-esp32c6-expansion_board/main/.build/release/_swift_package_app_main.o
FAILED: /Users/…/swift-embedded-xiao-esp32c6-expansion_board/main/.build/release/_swift_package_app_main.o
cd /Users/…/swift-embedded-xiao-esp32c6-expansion_board/build/esp-idf/main && /Users/…/.swiftly/bin/llvm-ar x /Users/…/swift-embedded-xiao-esp32c6-expansion_board/main/.build/release/libApp.a $( nm --defined-only -A /Users/…/swift-embedded-xiao-esp32c6-expansion_board/main/.build/release/libApp.a | grep -m 1 ' T app_main' | cut -d: -f2 ) --output /Users/…/swift-embedded-xiao-esp32c6-expansion_board/main/.build/release && ( mv -f -T /Users/…/swift-embedded-xiao-esp32c6-expansion_board/main/.build/release/$( nm --defined-only -A /Users/…/swift-embedded-xiao-esp32c6-expansion_board/main/.build/release/libApp.a | grep -m 1 ' T app_main' | cut -d: -f2 ) /Users/…/swift-embedded-xiao-esp32c6-expansion_board/main/.build/release/_swift_package_app_main.o 2>/dev/null || ( rm -rf /Users/…/swift-embedded-xiao-esp32c6-expansion_board/main/.build/release/_swift_package_app_main.o && mv -f /Users/…/swift-embedded-xiao-esp32c6-expansion_board/main/.build/release/$( nm --defined-only -A /Users/…/swift-embedded-xiao-esp32c6-expansion_board/main/.build/release/libApp.a | grep -m 1 ' T app_main' | cut -d: -f2 ) /Users/…/swift-embedded-xiao-esp32c6-expansion_board/main/.build/release/_swift_package_app_main.o ) )
mv: rename /Users/…/swift-embedded-xiao-esp32c6-expansion_board/main/.build/release/ to /Users/…/swift-embedded-xiao-esp32c6-expansion_board/main/.build/release/_swift_package_app_main.o: Invalid argument

Looking at the output from nm --defined-only -A /Users/…/swift-embedded-xiao-esp32c6-expansion_board/main/.build/release/libApp.a | grep app_main, it gives

/Users/…/swift-embedded-xiao-esp32c6-expansion_board/main/.build/release/libApp.a:App.swift.o: 00000000 W app_main

See the symbol type is W (Weak) instead of T (Text) that your script expects.

Updating the grep expression in main/CMakeLists.txt makes the build pass, and I could flash it to the board and it works as expected.

For information, I'm working on macOS 15.5, using Swift main-snapshot-2025-05-23 installed via Swiftly, ESP-IDF 5.4.1

Thank you for sharing! I'll investigate whether the App.swift.o symbol has some hidden linking rules.
In my recent commits, I added some code to App.swift that might be related to this issue.
The information you provided was very helpful – I'll use it to optimize my build scripts.