Importing C Libraries in Swift
find the code here https://github.com/Chandram-Dutta/SwiftRaylib/
After watching Tsoding struggle with importing a C Library (raylib) in his recent stream. I decided to make the process easier and more straightforward for developers. I totally agree that the official documentation is not very clear and lacks examples. This blog aims to address that issue.
So we start by creating a swift package using
mkdir SwiftRaylib
cd SwiftRaylib
swift package init --name SwiftRaylib --type executable
This initialises our SwiftRaylib
Package and the folder structure should look like this
SwiftRaylib/
├── Package.swift
└── Sources
└── main.swift
Now let’s move the C Library in the Sources directory. We will use the raylib 5.0 release for macOS.
Note that for Swift Package Manager(SPM) to include a C Library in project, the C Library should have a include directory with the header files present in it. In our case, raylib’s file structure looks like this
raylib-5.0_macos/
├── CHANGELOG
├── LICENSE
├── README.md
├── include
│ ├── raylib.h
│ ├── raymath.h
│ └── rlgl.h
└── lib
├── ...
where the include directory does contains all the necessary header files.
Now another important change to make is creating a new folder in Sources with the same name as that of the Swift Project and moving the main.swift
file inside it.
SwiftRaylib/
├── Package.swift
└── Sources
├── SwiftRaylib
│ └── main.swift
└── raylib-5.0_macos
And now the last step is to edit Package.swift
Presently Package.swift
should look like this
// swift-tools-version: 6.0
import PackageDescription
let package = Package(
name: "SwiftRaylib",
targets: [
.executableTarget(
name: "SwiftRaylib")
]
)
We are going to add a products
parameter that takes in an array of the C Library and the Swift Executable
// swift-tools-version: 6.0
import PackageDescription
let package = Package(
name: "SwiftRaylib",
products: [
.library(name: "raylib-5.0_macos", targets: ["raylib-5.0_macos"]),
.executable(
name: "SwiftRaylib",
targets: ["SwiftRaylib"]),
],
targets: [
.executableTarget(
name: "SwiftRaylib"),
]
)
Secondly we will modify the targets
by adding a target of our C Library and adding that as a dependency to our Swift executable target. We will also add a swiftSettings
that enables the interoperability between C and Swift.
// swift-tools-version: 6.0
import PackageDescription
let package = Package(
name: "SwiftRaylib",
products: [
.library(name: "raylib-5.0_macos", targets: ["raylib-5.0_macos"]),
.executable(
name: "SwiftRaylib",
targets: ["SwiftRaylib"]),
],
targets: [
.target(
name: "raylib-5.0_macos"),
.executableTarget(
name: "SwiftRaylib",
dependencies: ["raylib-5.0_macos"],
swiftSettings: [.interoperabilityMode(.Cxx)]),
]
)
Now running swift run
should compile and run the project.
To test we will import raylib in main.swift
and write a simple program using raymath.h
import raylib_5_0_macos
let v1 = Vector2(x: 3.0, y: 2.0)
let v2 = Vector2(x: 4.0, y: 7.0)
print("Distance between \(v1) and \(v2) is \(Vector2Distance(v1, v2))")
The output should be
Distance between Vector2(x: 3.0, y: 2.0) and Vector2(x: 4.0, y: 7.0) is 5.0990195
Note that Vector2
and Vector2Distance()
is being imported from the raylib
.
I wasn’t able to use any functions from
raylib.h
as it doesn’t support arm64(atleast that’s what I got from the compiler.
error: link command failed with exit code 1 (use -v to see invocation) Undefined symbols for architecture arm64: "_InitWindow", referenced from: _SwiftRaylib_main in main.swift.o ld: symbol(s) not found for architecture arm64 clang: error: linker command failed with exit code 1 (use -v to see invocation)
if someone knows more about this issue, please let me know!
Subscribe to my newsletter
Read articles from Chandram Dutta directly inside your inbox. Subscribe to the newsletter, and don't miss out.
Written by
Chandram Dutta
Chandram Dutta
swe intern. swift , flutter 💙, svelte 🧡, rust 🦀. he/him.