> On Sep 29, 2016, at 10:16 AM, Joe Groff via swift-evolution 
> <[email protected]> wrote:
> 
> In the discussion around SE-0139 (bridging NSNumber and NSValue), many people 
> pointed out that it's common in ObjC to use NSNumbers holding enum constants 
> in Cocoa containers. Many ObjC interfaces look something like this:
> 
>       /// A washing machine.
>       @interface QXWashingMachine
> 
>       /// Wash cycles supported by the washing machine.
>       typedef NS_ENUM(NSInteger, QXWashCycle) {
>         QXWashNormal,
>         QXWashDelicates,
>         QXWashLinens,
>       };
> 
>       /// Perform a sequence of wash cycles. Takes an NSArray of QXWashCycle 
> constants passed
>       /// as NSNumbers.
>       - (void)performWashCycles:(NSArray *)cycles;
> 
>       @end
> 
> In ObjC, you can call this API like this:
> 
>       [washingMachine performWashCycles:@[
>         @(QXWashLinens),
>         @(QXWashDelicates),
>       ]];
> 
> In Swift 3, the equivalent code will compile, but fail at runtime, because 
> the enum type falls back to opaque object bridging instead of using NSNumbers:
> 
>       // Fails at runtime, because WashCycle constants don't implicitly 
> bridge to NSNumber
>       washingMachine.perform(washCycles: [WashCycle.linens, 
> WashCycle.delicates])
> 
> so you have to know to get the `rawValue`s out first:
> 
>       // Fails at runtime, because WashCycle constants don't implicitly 
> bridge to NSNumber
>       washingMachine.perform(washCycles: [WashCycle.linens.rawValue, 
> WashCycle.delicates.rawValue])
> 
> We encountered similar problems last year as we developed the `swift_newtype` 
> ObjC feature last year, which enabled us to import some stringly-typed ObjC 
> APIs with stronger types in Swift. A type like `Notification.Name`, which 
> represents a set of NSString constants, is imported to be RawRepresentable as 
> String and also to bridge to Objective-C as one, by having the compiler 
> implicitly generate a conformance to the internal _ObjectiveCBridgeable 
> protocol for it. We could conceivably do one of the following things:
> 
> - Have the compiler generate a bridging conformance for imported NS_ENUM and 
> NS_OPTIONS types, and perhaps also for @objc enums defined in Swift, like we 
> do for newtypes.
> - Alternatively, we could say that *anything* with a RawRepresentable 
> conformance bridges to ObjC as its rawValue.
> 
> The second one is conceptually appealing to me, since RawRepresentable is 
> already something newtypes, enums, and option sets have in common in Swift, 
> but I think it may be too lax—it would effectively make RawRepresentable the 
> user interface to Objective-C bridging, which I'm not sure is something we 
> want. Limiting the bridging behavior to @objc enums and imported option sets 
> limits the impact, though there are still tradeoffs to consider:
> 
> - Adding any new bridging behavior has the potential to break existing code 
> that relies on the current opaque object bridging. We tell people not to do 
> that, of course, but that's no guarantee that people don't.
> - As with newtypes, the bridged Objective-C representation loses type 
> information from Swift, meaning that dynamic casts potentially need to become 
> "slushier". Swift maintains the distinction between Notification.Name and 
> String, for example, but once a value of either type has been bridged to 
> NSString, the distinction is lost, so we have to allow casts from NSString 
> back to either String or Notification.Name. If we bridge enums and option 
> sets, we would similarly lose type information once we go to NSNumber. We can 
> in some cases mitigate this for class clusters like NSString or NSNumber by 
> using our own subclasses that preserve type info, which can at least preserve 
> type info for Swift-bridged objects, though we can't do this universally for 
> all Cocoa-sourced objects.


Personally, I consider the first one to be a fairly-low-risk extension to 
SE-0139 that’s borderline bug-fix. We already know that those types have weak 
numeric representations in Objective-C because they come from Objective-C, so 
losing some of the type info by bridging to Objective-C is (IMO) falls out of 
having strong types in Swift for weaker types in Objective-C.

The second one makes me a little nervous, I think because it weakens typing for 
types defined in Swift. These types don’t naturally have Objective-C 
counterparts, so if we’re going to weaken the types, it feels like we should 
only do so via some explicit conformance (e.g., to a publicly-available form of 
_ObjectiveCBridgeable).

        - Doug

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

Reply via email to