diff --git a/CHANGELOG.md b/CHANGELOG.md index e33693487..af4e5ec55 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -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 diff --git a/fixtures/coverall/tests/bindings/test_coverall.swift b/fixtures/coverall/tests/bindings/test_coverall.swift index 084d4a589..a71687897 100644 --- a/fixtures/coverall/tests/bindings/test_coverall.swift +++ b/fixtures/coverall/tests/bindings/test_coverall.swift @@ -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() diff --git a/uniffi_bindgen/src/bindings/swift/gen_swift/mod.rs b/uniffi_bindgen/src/bindings/swift/gen_swift/mod.rs index 3b5f4404a..83b2ca6b1 100644 --- a/uniffi_bindgen/src/bindings/swift/gen_swift/mod.rs +++ b/uniffi_bindgen/src/bindings/swift/gen_swift/mod.rs @@ -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 { + 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 { let header = BridgingHeader::new(config, ci) diff --git a/uniffi_bindgen/src/bindings/swift/templates/ObjectTemplate.swift b/uniffi_bindgen/src/bindings/swift/templates/ObjectTemplate.swift index d045fad45..d85ec23cf 100644 --- a/uniffi_bindgen/src/bindings/swift/templates/ObjectTemplate.swift +++ b/uniffi_bindgen/src/bindings/swift/templates/ObjectTemplate.swift @@ -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.