Hello all,
This is a follow-up from an earlier discussion about bridging here.
After much consideration, we are going to go forward with implementing the "as" bridging for Swift on Linux. We have been exploring the concept of leaving this out since the beginning of the project. However, as we work towards finishing the remaining unimplemented features, we believe that the lack of parity in bridging is holding us back.
This missing feature causes some pretty mysterious behavior when porting code between platforms.
func f() -> Any {
return NSNumber(value: 5)
}
let r = f() as? Double
print("It's \(r)")
// Darwin: It's Optional(5.0)
// Linux: It's nil
The primary areas where this causes confusion are also some of our most popular APIs: JSON serialization and property lists. These are APIs where we return an Any
type and rely on the developer to cast to their desired result type.
JSONSerialization
in particular has been an interesting case study. On Darwin, it creates NSNumber
instances to represent the numeric values found in the source data. NSNumber
may be cast to many Swift numeric types, via bridging. Due to the lack of bridging combined with the lack of an equivalent "AnyNumber" type in the standard library, JSONSerialization
on Linux is implemented to return a specific number type (Double
or Int
) depending on what is detected in the JSON. This can be surprising if the rest of the code expects one type or the other.
Other API relies on reference semantics. The most common example is NSKeyedArchiver
, which has explicit class
API like decodeObject(ofClass:forKey:)
. Archiving also supports cyclical references, decoding mutable shared values, replacement of values, and more. While we believe that Codable
is likely the future of Swift-friendly archiving, there is an immense amount of customer data stored in keyed archives. We believe it is important to continue to support reading and writing those files.
We considered several alternatives:
Remove all reference types from Foundation: This prevents the possibility of writing code which has a mysterious bridging failure, but it also disallows usage of important API like NSKeyedArchiver
. Furthermore, if we add value type equivalents to more reference types in the future, should we remove those reference types too? If the answer is yes, we are setting people up to have API removed from underneath them. If the answer is no, then we will have an unprincipled mix of reference types that do and do not exist.
Furthermore, many Swift projects do use some Foundation reference types today. Yanking the reference types would make us instantly incompatible with those projects, should they choose to port to non-Darwin platforms.
Add an AnyNumber
type to the standard library: This may be an effective solution to the NSNumber
bridging problem above, but it requires adoption in both the implementations that produce numeric types today and the clients which receive them. We think this is an interesting future direction independent of the bridging discussion.
Add a Box
type to the standard library: A standard Box
type may be useful to replace some of the reference types in Foundation where the only requirement is reference semantics. We think this is also an interesting future direction, but also requires adoption across both implementations and client source code.
@millenomi has been working on implementing this and will have a PR ready soon.