[swift-evolution] [Idea] ObjectiveCBridgeable

Douglas Gregor dgregor at apple.com
Thu Mar 24 01:17:46 CDT 2016

> On Mar 23, 2016, at 1:25 PM, Russ Bishop <xenadu at gmail.com> wrote:
>> On Mar 23, 2016, at 11:49 AM, Douglas Gregor <dgregor at apple.com <mailto:dgregor at apple.com>> wrote:
>>> I assume this is a static function to avoid allocating memory by calling the initializer directly for each element, given the point is to defer the work? I wonder if we can skip the static though and just call the initializer directly? It would simplify the protocol a tiny bit.
>> From an implementation perspective, the entry point for an initializer in a protocol handles the allocation itself. It’s a static function because it was easy to implement that way and the actual definitions get a bit more flexibility in how they can come up with the object (since we don’t have factory initializers).
> Good point about the factory initializers. I’ve been trying to keep the whole “class cluster” situation in mind. I wonder if there should be an equivalent static function for the conditional bridging for similar reasons?

I realize that my comment about factory initializers was a bit off: these are initializers on the value type, so there are no “class clusters” to be had. Sorry for the noise!

> I added a separate section on Ambiguity and what the behavior is. I think you should be able to resolve ambiguity by casting so I went ahead and put that in. An example:
> //Bar and Foo bridge to SomeObjectiveCType
> struct Bar<T>: ObjectiveCBridgeable { }
> struct Foo<T>: ObjectiveCBridgeable { }
> class API {
>     let foo: Foo<Int>
>     func objCVersionOfAFunction(obj: SomeObjectiveCType) -> SomeObjectiveCType {
>         let x = obj as! Bar<Int>
>         // We've told the compiler which protocol impl to call
>         return foo as! SomeObjectiveCType
>     }
> }
> Any problems with this approach? It makes handling the ambiguous or manual bridging case relatively straightforward, though there may be objections to using casting this way. [Be careful, I still mourn the loss of @conversion so I’m biased :)]

The problem I have with allowing the ambiguity is that you can get weird behavior if Bar and Foo are in different modules: import just Bar’s module, and an Objective-C API mentioning SomeObjectiveCType gets bridged as a Bar. Import just Foo’s module, and an Objective-C API mentioning SomeObjectiveCType gets bridged as a Foo. Import both, and SomeObjectiveCType doesn’t get bridged! Now start splitting class hierarchies among those modules and you get some very inconsistent imports… that’s why I think this needs to be an error.

> `NSInteger` is already imported as `Int` from Objective-C right? I assume a Clang attribute is specifying that somewhere but there wouldn’t be any ambiguity on the Swift side during import.

It’s hardcoded, but it’s completely reasonable to imagine swift_bridge doing this some day.

> I could definitely see having an attribute to declare that this specific parameter or return value should bridge to a specific Swift type (this NSNumber should import as Int) but that’s a lot of work and may be worth a separate proposal.

It would be hard for me to get motivated for such a proposal; at that point, just wrap up the API.

> It seems like the problem is going the other direction: you want to materialize this parameter or whatever as `NSInteger` instead of the default `NSNumber *` but only when directly bridged, not inside collections. There’s no existing Objective-C header to tell us what to do. I’m not sure how we can resolve this without a Swift attribute to tell the compiler because the handling of it would be specific to each declaration. 

I don’t have a good answer here; today, it’s hardcoded.

> We could just  say that we aren’t going to let people have that level of granularity. Then just introduce a BuiltInBridgeable protocol that supersedes ObjectiveCBridgeable. A type adopting both will cause the compiler to prefer the built-in protocol when generating a bridging header, but the collection types can ignore that and just use ObjectiveCBridgeable. Presumably the BuiltInBridgeable protocol would just have an associated type to indicate that the bits are directly mapped to BuiltIn.Word or whatever.

Yes, that’s a reasonable approach. I don’t think it’s important for this proposal.

Two last comments came up, then I’d like to merge and schedule this:

(1) isBridgedToObjectiveC should be a computed property, not a function

(2) Please add something indicating that, while one can “as” cast between a value type and its bridged Objective-C type, there are no implicit conversions. We currently have implicit conversions from String -> NSString, Array<T> -> NSArray, Dictionary<K, V> -> NSDictionary, and Set<T> -> NSSet, but we’re not happy about them and we don’t want to create more implicit conversions [*].

	- Doug

[*] The fact that you mourn the loss of @conversion did not go unnoticed as I was writing this ;)

-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://lists.swift.org/pipermail/swift-evolution/attachments/20160323/09d7c6a7/attachment.html>

More information about the swift-evolution mailing list