Closed Kiwijane3 closed 4 years ago
Generally, this should work, as long as the type hierarchy is recorded correctly in the .gir
files. For example, see the following snippet from SwiftHelloGtk:
let label = Label(str: "Hello, SwiftGtk")
window.add(widget: label)
This works, because a Label is also a Widget. As long as a type implements a given interface (e.g. a Label
class that's a subclass of Widget
), this should work. Can you give a specific example where this didn't work for you?
The issue specifically occurs when using a variable declared as conforming to a protocol; Label works because it's declared as a concrete type. It it was instead explicitly declared as being of type LabelProtocol, the error would occur. Being able to use these protocols in types declarations is useful for instances where reference counted and non-reference counted types might be used; For instance, I encountered this issue in a MVC framework I'm developing, where I tried to use WidgetProtocol as the type of a controller's root widget.
Yes, that's also possible: the most efficient way is to use a generic. This way the compiler will know what the concrete type is and doesn't incur the extra overhead of using a Protocol witness table. If you really want to full type erasure (with the overhead this brings), you can do still do this, e.g.:
let someLabel = Label(str: "Hello, SwiftGtk")
// ...
let anyWidget: WidgetProtocol = someLabel
window.add(widget: WidgetRef(anyWidget.widget_ptr))
Wrapping the pointer from anyWidget
in a WidgetRef
is necessary, because in Swift, Protocols don't conform to themselves. In practice, a WidgetRef
is just a type-safe version of the underlying pointer, so this should not create any extra code (other than looking up and calling the getter for widget_ptr
in the Protocol witness table). IMHO a better way that should give you almost the same benefits but get rid of all the witness table overhead is to use WidgetRef
directly, e.g.:
let someLabel = Label(str: "Hello, SwiftGtk")
// ...
let widgetRef: WidgetRef = WidgetRef(someLabel)
window.add(widget: widgetRef)
The only thing you will lose with this approach is the ability to do run-time introspection, i.e. anyWidget is Label
will return true
, but widgetRef is Label
will return false
.
[Edit: fix typo]
Of course, the following code above
let widgetRef: WidgetRef = WidgetRef(someLabel)
was only written that way to make clear what the types are, so you can rewrite this as:
let widgetRef = WidgetRef(someLabel)
Right, that makes sense from an optimisation perspective. I'll continue using the reference-counted versions in the controllers; It makes sense for a controller to have a reference, in my view.
Yes, if you want Swift to do the reference-counting for you, use Widget
(not WidgetRef
, where you would have to call ref()
and unref()
yourself). You can also combine this with weak
if you don't want to own the widget, e.g.:
weak var weakWidget: Widget? = someLabel
In the current version of the wrapper, it isn't possible to use variables defined as WidgetProtocol, or similar protocol types, because of the typing produced by gir2swift. A variety of functions do not work with Protocols, because they are defined as generics, and protocols cannot fulfill generic constraints, and they cannot be directly set as variables, because variables are defined asRef. This issue could likely be resolved by redefining functions and variables to simply use the appropriate protocols directly, unless generics are specifically needed. The issue can be worked around instantiating a Ref using the appropriate pointer property of the variable.