Skip to content

Commit

Permalink
Swift classes can inherit from traits
Browse files Browse the repository at this point in the history
Builds on #2196, #2204 and #2297. Closes #2169.
  • Loading branch information
mhammond committed Dec 22, 2024
1 parent b366d37 commit 945d38f
Show file tree
Hide file tree
Showing 3 changed files with 47 additions and 1 deletion.
21 changes: 21 additions & 0 deletions fixtures/coverall/tests/bindings/test_coverall.swift
Original file line number Diff line number Diff line change
Expand Up @@ -464,6 +464,27 @@ do {
traits[0].setParent(parent: nil)
}

// A struct which implements the node trait.
do {
let n = Node(name: "node")
assert(String(describing: n).starts(with: "Node { name: Some(\"node\"), parent: Mutex { "))
assert(n.getParent()?.name() == "via node")

n.setParent(parent: n.getParent())
// doubly-wrapped :(
// Get: "Some(UniFFICallbackHandlerNodeTrait { handle: 19 })"
// Want: Like the Rust node above.
// debugPrint("parent \(n.describeParent())")

let rustParent = Node(name: "parent")
n.setParent(parent: rustParent)
assert(n.getParent()?.name() == "parent")

let swiftParent = SwiftNode()
rustParent.setParent(parent: swiftParent)
assert(ancestorNames(node: n) == ["parent", "node-swift"])
}

// Test round tripping
do {
let rustGetters = makeRustGetters()
Expand Down
21 changes: 21 additions & 0 deletions uniffi_bindgen/src/bindings/swift/gen_swift/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -272,6 +272,27 @@ impl Config {
}
}

// Given a trait, work out what the protocol name we generate for it.
// This differs based on whether the trait supports foreign impls (ie,
// whether is has a "callback interface".
fn trait_protocol_name(ci: &ComponentInterface, name: &str) -> Result<String> {
let (obj_name, has_callback_interface) = match ci.get_object_definition(name) {
Some(obj) => (obj.name(), obj.has_callback_interface()),
None => (
ci.get_callback_interface_definition(name)
.ok_or_else(|| anyhow::anyhow!("no interface {}", name))?
.name(),
true,
),
};
let class_name = SwiftCodeOracle.class_name(obj_name);
if has_callback_interface {
Ok(class_name)
} else {
Ok(format!("{class_name}Protocol"))
}
}

/// Generate UniFFI component bindings for Swift, as strings in memory.
pub fn generate_bindings(config: &Config, ci: &ComponentInterface) -> Result<Bindings> {
let header = BridgingHeader::new(config, ci)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,11 @@ open class {{ impl_class_name }}:
{%- if is_error %}
Swift.Error,
{% endif %}
{{ protocol_name }} {
{%- for t in obj.trait_impls() %}
{{ self::trait_protocol_name(ci, t.trait_name)? }},
{% endfor %}
{{ protocol_name }}
{
fileprivate let pointer: UnsafeMutableRawPointer!

/// Used to instantiate a [FFIObject] without an actual pointer, for fakes in tests, mostly.
Expand Down

0 comments on commit 945d38f

Please sign in to comment.