[swift-evolution] [Pitch] Making some RawRepresentable things bridge to ObjC as their raw value

Zach Waldowski zach at waldowski.me
Thu Sep 29 13:25:22 CDT 2016

I think limiting to types that are both @objc and RawRepresentable is a
nice compromise; that limits the type preservation needed to work around
"slushiness" to NSString, NSNumber (already done), and NSError.

I know your pitch doesn't cover this problem, but I would also note that
C varargs suffer from the same "needing to know to get the rawValue"

  Zachary Waldowski
  zach at waldowski.me

On Thu, Sep 29, 2016, at 10:16 AM, Joe Groff via swift-evolution 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.
> -Joe
> _______________________________________________
> swift-evolution mailing list
> swift-evolution at swift.org
> https://lists.swift.org/mailman/listinfo/swift-evolution

More information about the swift-evolution mailing list