> On Mar 23, 2016, at 12:21 AM, Russ Bishop <[email protected]> wrote:
> 
>> 
>> On Mar 21, 2016, at 9:41 AM, Douglas Gregor <[email protected] 
>> <mailto:[email protected]>> wrote:
>> 
>> 
>>> On Mar 9, 2016, at 12:26 PM, Russ Bishop via swift-evolution 
>>> <[email protected] <mailto:[email protected]>> wrote:
>>> 
>>> An official proposal PR has been opened on this: 
>>> https://github.com/apple/swift-evolution/pull/198 
>>> <https://github.com/apple/swift-evolution/pull/198>
>>> 
>>> It includes clarifications, a few changes, and some better examples.
> 
>> 
>>     /// Returns `true` iff instances of `Self` can be converted to
>>     /// Objective-C.  Even if this method returns `true`, a given
>>     /// instance of `Self._ObjectiveCType` may, or may not, convert
>>     /// successfully to `Self`.
>>     ///
>>     /// A default implementation returns `true`.
>>     @warn_unused_result
>>     static func isBridgedToObjectiveC() -> Bool
>> It’s probably worth saying why someone might override this method: usually, 
>> it’s because the Swift type is generic and it only makes sense to bridge for 
>> some type arguments. Granted, there is no way to communicate this 
>> information to the compiler, which is a bit of a hole in the design. For 
>> example, Array<T> only bridges to NSArray when T is itself representable in 
>> Objective-C. We really need conditional conformances to for this part of the 
>> feature to work properly.
> 
> I’ve added a section to the proposal to call out that it is intended for 
> conformance to be conditional in some cases (eg Array), and a small 
> explanation of what to do and a code example. It’s a bit unwieldy but 
> workable until we have conditional conformance.

Great. The suggestion to use an extension won’t actually work:

    /// A default implementation returns `true`. If a Swift type is 
    /// generic and should only be bridged for some type arguments,
    /// provide alternate implementations in extensions 
    /// and return `false` in those cases.
    ///
    ///     struct Foo<T>: ObjectiveCBridgeable { ... }
    ///     extension Foo where T: NonBridgedType {
    ///         static func isBridgedToObjectiveC() -> Bool { 
    ///             return false 
    ///         }
    ///     }
because you don’t get dynamic dispatching based on T. Instead, you want one 
implementation of isBridgedToObjectiveC that does a dynamic check, e.g.,

struct Foo<T>: ObjectiveCBridgeable {
  static func isBridgedToObjectiveC() -> Bool {
    return !(T is NonBridgedType)
  }
}
  

>> 
>>     /// Try to construct a value of the Self type from
>>     /// an Objective-C object of the bridged class type.
>>     ///
>>     /// If the conversion fails this initializer returns `nil`.
>>     init?(bridgedFromObjectiveC: ObjectiveCType)
>> FWIW, implementing this required a new “unconditional” entry point used by 
>> the compiler:
>> 
>>   static func _unconditionallyBridgeFromObjectiveC(source: _ObjectiveCType?)
>>       -> Self
>> 
>> It can get a default implementation, and should be a non-failable 
>> initializer.
> 
> I’ve updated the proposal to add this. The default implementation of 
> "init(unconditionallyBridgedFromObjectiveC: ObjectiveCType?)" just calls the 
> fallible initializer and aborts if anything goes wrong. Default 
> implementation of “unconditionallyBridgeFromObjectiveC(source: 
> ObjectiveCType?)” calls the initializer. My guess is most types will probably 
> just accept the default behavior.

Yes, I suspect you’re right that most types will use the default.

> 
> 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).

>>      4. The ObjectiveCType must be defined in Swift. If a -swift.h header is 
>> generated, it will include a SWIFT_BRIDGED()macro where the parameter 
>> indicates the Swift type with which the ObjectiveCType bridges. The macro 
>> will be applied to the ObjectiveCType and any subclasses.
>> 
>> This is unnecessarily restrictive, and eliminates the “make my Objective-C 
>> class bridge into a Swift value type” case that (for example) the compiler 
>> already does for String/Array/Dictionary/Set. I think there are two cases:
>> 
>>      (a) The ObjectiveCType is defined in Objective-C. It must be an 
>> Objective-C class with the attribute swift_bridge(“Bar”), where “Bar” is the 
>> name of the bridged Swift type.
>>      (b) The ObjectiveCType is defined in Swift, in which case it must be an 
>> @objc class. When emitting the generated header, the appropriate 
>> swift_bridge attribute will be added to the @interface declaration.
>> 
>> 
> 
> I mostly hadn’t really considered the implications but I see the value and I 
> trust your assessment; I removed the restriction and reworded the whole 
> section.

Looks good, thanks!

>> 
>> (This was #5) It is an error for bridging to be ambiguous.
>> A Swift type may bridge to an Objective-C base class, then provide different 
>> subclass instances at runtime but no other Swift type may bridge to that 
>> base class or any of its subclasses.
>> The compiler must emit a diagnostic when it detects two Swift types 
>> attempting to bridge to the same ObjectiveCType.
>> This is a tricky area. Currently, Int/Float/Double/Bool/CGFloat/UInt all 
>> have _ObjectiveCBridgeable conformances, although those conformances only 
>> really kick in at runtime (e.g., when dynamically casting an [AnyObject] or 
>> [NSNumber] to [Int] or [Double] with as? or matching a switch case). They 
>> would run afoul of this rule. However, this rule does generally make sense: 
>> if two Swift types have the same ObjectiveCType, we won’t know how to map an 
>> Objective-C API back into Swift. Those numeric types only work because they 
>> are trivially mapped between Swift and (Objective-)C; they don’t need to go 
>> through the _ObjectiveCBridgeable conformance. 
> 
> 
> Perhaps the rule should simply be that any Objective-C API imported that has 
> ambiguity is imported as the Objective-C type without automatic bridging 
> support. The compiler can continue to import Int and friends with special 
> magic. Creating an ambiguity just leaves you to resolve the problem manually? 
> The rule would be something like "omitting the SWIFT_BRIDGED() attribute from 
> ObjC //or// multiple Swift types bridging to the same ObjC type" turns off 
> automatic thunk generation but bridged collections will still call the 
> protocol where appropriate.

Yeah. Thinking about it a bit further, I like your rule basically as you’ve 
stated it, because having two _ObjectiveCBridgeable conformances mapping to the 
same type is a problem the user should have to resolve. I guess I’m just 
looking for something small to indicate that a direct mapping of the 
representation (e.g., NSInteger -> Int) supersedes the _ObjectiveCBridgeable 
conformance when mapping APIs between Objective-C and Swift.


> An update has been posted to 
> https://github.com/russbishop/swift-evolution/blob/master/proposals/0000-objectivecbridgeable.md
>  
> <https://github.com/russbishop/swift-evolution/blob/master/proposals/0000-objectivecbridgeable.md>
Thanks!

> Would you prefer if I did / did not add your name to the proposal? I feel 
> guilty taking all the credit.

I don’t have a strong preference. Feel free to add my name if you’d like. I 
really appreciate your work on driving this proposal forward!

        - Doug


_______________________________________________
swift-evolution mailing list
[email protected]
https://lists.swift.org/mailman/listinfo/swift-evolution

Reply via email to