> 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