On older OSs, CLLocationCoordinate2D was an anonymous struct with a
typedef, which meant its Objective-C type encoding string was "{?=dd}"
instead of "{CLLocationCoordinate2D=dd}". This incompatibility led to
us rejecting casts from NSValues created with CLLocationCoordinate2Ds
in Objective-C on older OSs because they didn't match the type
encoding provided. Instead, we can try to create an NSValue the way
the frameworks do, and see what /its/ type encoding is. This is what
SceneKit already does.
There's an extra wrinkle here because the convenience methods for
encoding CLLocationCoordinate2Ds are actually added in MapKit rather
than CoreLocation. That means that if someone's app just uses
CoreLocation, we can't rely on those convenience methods to get the
correct type encoding. In this case, the best thing we can do is just
give up and use the static encoding.
This whole thing is indicative of a bigger problem, namely that
NSValue normally doesn't try to validate types at all. However, Swift
bridge casting really does want to distinguish, say, a
CLLocationCoordinate2D from a CGPoint, and checking the NSValue encode
string was a way to do that. But this shows that it's still not safe
to assume that's consistent against everything in a process even if
they're all using @encode on the real struct (or the Swift
equivalent), because the different parts of the process may have been
compiled against different SDKs.
This change does not attempt to solve that problem.
Finishes rdar://problem/44866579
For every struct type for which the frameworks provides an NSValue category for boxing and unboxing values of that type, provide an _ObjectiveCBridgeable conformance in the Swift overlay that bridges that struct to NSValue, allowing the structs to be used naturally with id-as-Any APIs and Cocoa container classes. This is mostly a matter of gyb-ing out boilerplate using `NSValue.init(bytes:objCType:)` to construct the instance, `NSValue.objCType` to check its type when casting, and `NSValue.getValue(_:)` to extract the unboxed value, though there are a number of special snowflake cases that need special accommodation:
- To maintain proper layering, CoreGraphics structs need to be bridged in the Foundation overlay.
- AVFoundation provides the NSValue boxing categories for structs owned by CoreMedia, but it does so using its own internal subclasses of NSValue, and these subclasses do not interop properly with the standard `NSValue` subclasses instantiated by Foundation. To do the right thing, we therefore have to let AVFoundation provide the bridging implementation for the CoreMedia types, and we have to use its category methods to do so.
- SceneKit provides NSValue categories to box and unbox SCNVector3, SCNVector4, and SCNMatrix4; however, the methods it provides do so in an unusual way. SCNVector3 and SCNVector4 are packaged into `CGRect`s and then the CGRect is boxed using `valueWithCGRect:`. SCNMatrix4 is copied into a CATransform3D, which is then boxed using `valueWithCATransform3D:` from CoreAnimation. To be consistent with what SceneKit does, use its category methods for these types as well, and when casting, check the type against the type encoding SceneKit uses rather than the type encoding of the expected type.