Skip to content

Commit

Permalink
Swift classes can inherit from traits (#2363)
Browse files Browse the repository at this point in the history
Builds on #2196, #2204 and #2297. Closes #2169.
  • Loading branch information
mhammond authored Dec 24, 2024
1 parent 1c002fa commit 8185a28
Show file tree
Hide file tree
Showing 4 changed files with 50 additions and 3 deletions.
5 changes: 3 additions & 2 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,8 +33,9 @@ We have [detailed upgrade notes](https://mozilla.github.io/uniffi-rs/next/Upgrad
[Detailed upgrade notes](https://mozilla.github.io/uniffi-rs/next/Upgrading.html)
### What's new?

- Kotlin: Proc-macros exporting an `impl Trait for Struct` block now has a class inheritance
hierarcy to reflect that. [#2297](https://github.com/mozilla/uniffi-rs/pull/2297)
- Kotlin and Swift: Proc-macros exporting an `impl Trait for Struct` block now has a class inheritance
hierarcy to reflect that.
[#2297](https://github.com/mozilla/uniffi-rs/pull/2297), [#2363](https://github.com/mozilla/uniffi-rs/pull/2363)

- Removed the `log` dependency and logging statements about FFI calls. These were not really useful
to consumers and could have high overhead when lots of FFI calls are made. Instead, the
Expand Down
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 8185a28

Please sign in to comment.