[swift-evolution] [Proposal draft] Bridge Numeric Types to NSNumber and Cocoa Structs to NSValue

Joe Groff jgroff at apple.com
Mon Aug 29 11:09:49 CDT 2016

> On Aug 23, 2016, at 4:00 PM, Tony Parker via swift-evolution <swift-evolution at swift.org> wrote:
> Hi Doug,
>> On Aug 23, 2016, at 3:36 PM, Douglas Gregor via swift-evolution <swift-evolution at swift.org> wrote:
>> Introduction
>> A handful of Swift numeric types are bridged to NSNumber when passed into Objective-C object contexts. We should extend this bridging behavior to all Swift numeric types. We should also bridge common Cocoa structs such as NSRangeby boxing them into NSValue objects.
>> Swift-evolution thread: TBD
>> Motivation
>> SE-0116 changed how Objective-C's id and untyped collections import into Swift to use the Any type. This makes it much more natural to pass in Swift value types such as String and Array, but introduces the hazard of passing in types that don't bridge well to Objective-C objects. Particularly problematic are number types; whereas Int, UInt, and Double will automatically bridge as NSNumber, other-sized numeric types fall back to opaque boxing:
>> let i = 17
>> let plist = ["seventeen"
>> : i]
>> // OK
>> try! NSJSONSerialization.
>> data(withJSONObject: plist)
>> let j: UInt8 = 38
>> let brokenPlist = ["thirty-eight"
>> : j]
>> // Will throw because `j` didn't bridge to a JSON type
>> try! NSJSONSerialization.data(withJSONObject: brokenPlist)
>> We had shied away from enabling this bridging for all numeric types in the Swift 1.x days, among other reasons because we allowed implicit bridging conversions in both directions from Swift value types to NS objects and back, which meant that you could slowly and brokenly convert between any two numeric types transitively via NSNumber if we allowed this. We killed the implicit conversions completely with SE-0072 so that is no longer a concern, so expanding the bridging behavior should no longer be a major problem, since it must now always be explicitly asked for.
>> There are also many Cocoa APIs that accept NSArray and NSDictionary objects with members that are NSValue-boxed structs. Matt Neuberg highlights Core Automation as an example in this bug report. With id-as-Any, it's natural to expect this to work:
>> anim.toValue = CGPoint.zero
>> However, the CGPoint value does not box as a meaningful Objective-C object, so this currently breaks Core Animation at runtime despite compiling successfully. It would be more idiomatic to bridge these types to NSValue.
>> Proposed solution
>> All of Swift's number types should be made to bridge to NSNumber when used as objects in Objective-C:
>> 	• Int8
>> 	• Int16
>> 	• Int32
>> 	• Int64
>> 	• UInt8
>> 	• UInt16
>> 	• UInt32
>> 	• UInt64
>> 	• Float
>> 	• Double
>> Cocoa structs with existing NSValue factory and property support should be made to bridge to NSValue when used as objects:
>> 	• NSRange
>> 	• CGPoint
>> 	• CGVector
>> 	• CGSize
>> 	• CGRect
>> 	• CGAffineTransform
>> 	• UIEdgeInsets
>> 	• UIOffset
>> 	• CATransform3D
>> 	• CMTime
>> 	• CMTimeRange
>> 	• CMTimeMapping
>> 	• MKCoordinate
>> 	• MKCoordinateSpan
>> 	• SCNVector3
>> 	• SCNVector4
>> 	• SCNMatrix4
> How do new types get added to this list? It’s certainly not the case that NSValue knows about them specifically; usually some kind of category is added after the fact.

I pulled this list from the API docs. Zach mentioned the existence of the `objc_boxable` attribute in Objective-C. Would it make more sense to make this an importer feature, introducing the bridging to NSValue based on that attribute rather than hard-coding the conformances in the overlay?

> Also, how does this bridging work for swift-corelibs-foundation on Linux, where bridging is disabled?

How much corelibs code works directly with NSNumber or NSValue? In native Swift code, you ought to be able to pass around Anys containing value types and dynamically cast out their values with Swift's native casting features.


More information about the swift-evolution mailing list