-
Notifications
You must be signed in to change notification settings - Fork 71
Description
I ran into a subtle bug that took me a while to track down. I presume the fix is not too hard, but I have not looked deep.
I have the setup where I call from Swift into Rust, but I want to hand a callback into Swift to rust. The pattern I choose for this looks like this:
#[swift_bridge::bridge]
mod ffi {
extern "Swift" {
type Observer;
fn on_update(&self, path: &str);
}
extern "Rust" {
type Actor;
fn do_some_stuff(&mut self, observer: Observer);
}
}
// some rust code implementing Actor and calling `on_update`I create a swift package after code gen using swift-bridge-cli create-package; I then drop a tiny .swift file into the Sources dir that contains a dummy implementation of Observer. My real code depends on this Swift Package as a local dependency.
This worked wonderfully for Debug builds, however for Release builds I got linker errors, __swift_bridge__$Observer$on_update and __swift_bridge__$Observer$free could not be found. I tracked down that toggling ENABLE_TESTABILITY=YES made the code compile again. After some ChatGPTing I came to the understanding that without this, the symbols are stripped from the compiler. The solution was pretty simple: just go over the generated code and make all functions tagged with @_cdecl public.
As a workaround I am currently using this python script.
#!/usr/bin/env python3
"""
add_public_to_swift_bridge.py <swift‑bridge file>
For every line that starts with “@_cdecl” the script inserts
“public ” at the beginning of the very next line, preserving whatever
indentation was already there. It edits the file in‑place.
"""
import re
import sys
from pathlib import Path
def main(path: Path) -> None:
text = path.read_text()
lines = text.splitlines(keepends=True)
for i, line in enumerate(lines[:-1]):
if re.match(r"^@_cdecl", line):
if not lines[i + 1].lstrip().startswith("public "):
lines[i + 1] = f"public " + lines[i + 1]
path.write_text("".join(lines))
if __name__ == "__main__":
if len(sys.argv) != 2:
sys.exit(
"usage: add_public_to_swift_bridge.py path/to/taskpaper_bridge.swift"
)
main(Path(sys.argv[1]))Thank you so much for this software - it is a lot of fun to use and very useful indeed!