Sent from my iPhone

> On 16 Mar 2017, at 01:18, Joe Groff via swift-evolution 
> <[email protected]> wrote:
> 
> Congrats on getting this out! A question from the field:
> 
> https://twitter.com/mdiep/status/842178457115230210 Why does the Swift 
> Serialization API proposal use abstract base classes?
> 

Hopefully because we realise we need abstract classes as a feature ;)? 

> -Joe
> 
> 
>> On Mar 15, 2017, at 3:40 PM, Itai Ferber via swift-evolution 
>> <[email protected]> wrote:
>> 
>> Hi everyone,
>> 
>> The following introduces a new Swift-focused archival and serialization API 
>> as part of the Foundation framework. We’re interested in improving the 
>> experience and safety of performing archival and serialization, and are 
>> happy to receive community feedback on this work.
>> Because of the length of this proposal, the Appendix and Alternatives 
>> Considered sections have been omitted here, but are available in the full 
>> proposal on the swift-evolution repo. The full proposal also includes an 
>> Unabridged API for further consideration.
>> 
>> Without further ado, inlined below.
>> 
>> — Itai
>> 
>> Swift Archival & Serialization
>>    • Proposal: SE-NNNN
>>    • Author(s): Itai Ferber, Michael LeHew, Tony Parker
>>    • Review Manager: TBD
>>    • Status: Awaiting review
>>    • Associated PRs:
>>        • #8124
>>        • #8125
>> Introduction
>> Foundation's current archival and serialization APIs (NSCoding, 
>> NSJSONSerialization, NSPropertyListSerialization, etc.), while fitting for 
>> the dynamism of Objective-C, do not always map optimally into Swift. This 
>> document lays out the design of an updated API that improves the developer 
>> experience of performing archival and serialization in Swift.
>> 
>> Specifically:
>> 
>>    • It aims to provide a solution for the archival of Swift struct and enum 
>> types
>>    • It aims to provide a more type-safe solution for serializing to 
>> external formats, such as JSON and plist
>> Motivation
>> The primary motivation for this proposal is the inclusion of native Swift 
>> enum and struct types in archival and serialization. Currently, developers 
>> targeting Swift cannot participate in NSCoding without being willing to 
>> abandon enum and structtypes — NSCoding is an @objc protocol, conformance to 
>> which excludes non-class types. This is can be limiting in Swift because 
>> small enums and structs can be an idiomatic approach to model 
>> representation; developers who wish to perform archival have to either forgo 
>> the Swift niceties that constructs like enumsprovide, or provide an 
>> additional compatibility layer between their "real" types and their 
>> archivable types.
>> 
>> Secondarily, we would like to refine Foundation's existing serialization 
>> APIs (NSJSONSerialization and NSPropertyListSerialization) to better match 
>> Swift's strong type safety. From experience, we find that the conversion 
>> from the unstructured, untyped data of these formats into strongly-typed 
>> data structures is a good fit for archival mechanisms, rather than taking 
>> the less safe approach that 3rd-party JSON conversion approaches have taken 
>> (described further in an appendix below).
>> 
>> We would like to offer a solution to these problems without sacrificing ease 
>> of use or type safety.
>> 
>> Agenda
>> This proposal is the first stage of three that introduce different facets of 
>> a whole Swift archival and serialization API:
>> 
>>    • This proposal describes the basis for this API, focusing on the 
>> protocols that users adopt and interface with
>>    • The next stage will propose specific API for new encoders
>>    • The final stage will discuss how this new API will interop with 
>> NSCoding as it is today
>> SE-NNNN provides stages 2 and 3.
>> 
>> Proposed solution
>> We will be introducing the following new types:
>> 
>>    • protocol Codable: Adopted by types to opt into archival. Conformance 
>> may be automatically derived in cases where all properties are also Codable.
>>    • protocol CodingKey: Adopted by types used as keys for keyed containers, 
>> replacing String keys with semantic types. Conformance may be automatically 
>> derived in most cases.
>>    • protocol Encoder: Adopted by types which can take Codable values and 
>> encode them into a native format.
>>        • class KeyedEncodingContainer<Key : CodingKey>: Subclasses of this 
>> type provide a concrete way to store encoded values by CodingKey. Types 
>> adopting Encoder should provide subclasses of KeyedEncodingContainer to vend.
>>        • protocol SingleValueEncodingContainer: Adopted by types which 
>> provide a concrete way to store a single encoded value. Types adopting 
>> Encoder should provide types conforming to SingleValueEncodingContainer to 
>> vend (but in many cases will be able to conform to it themselves).
>>    • protocol Decoder: Adopted by types which can take payloads in a native 
>> format and decode Codable values out of them.
>>        • class KeyedDecodingContainer<Key : CodingKey>: Subclasses of this 
>> type provide a concrete way to retrieve encoded values from storage by 
>> CodingKey. Types adopting Decoder should provide subclasses of 
>> KeyedDecodingContainer to vend.
>>        • protocol SingleValueDecodingContainer: Adopted by types which 
>> provide a concrete way to retrieve a single encoded value from storage. 
>> Types adopting Decoder should provide types conforming to 
>> SingleValueDecodingContainer to vend (but in many cases will be able to 
>> conform to it themselves).
>> For end users of this API, adoption will primarily involve the Codable and 
>> CodingKey protocols. In order to participate in this new archival system, 
>> developers must add Codable conformance to their types:
>> 
>> // If all properties are Codable, implementation is automatically derived:
>> public struct Location : Codable {
>> 
>> 
>> public let latitude: Double
>> 
>> 
>> public let longitude: Double
>> }
>> 
>> 
>> 
>> public enum Animal : Int, Codable {
>> 
>> 
>> case chicken = 1
>> 
>> 
>> case
>> dog
>> 
>> case
>> turkey
>> 
>> case
>> cow
>> 
>> }
>> 
>> 
>> 
>> public struct Farm : Codable {
>> 
>> 
>> public let name: String
>> 
>> 
>> public let location: Location
>> 
>> 
>> public let animals: [Animal]
>> }
>> With developer participation, we will offer encoders and decoders (described 
>> in SE-NNNN, not here) that take advantage of this conformance to offer 
>> type-safe serialization of user models:
>> 
>> let farm = Farm(name: "Old MacDonald's Farm",
>> 
>>                location
>> : Location(latitude: 51.621648, longitude: 0.269273),
>> 
>>                animals
>> : [.chicken, .dog, .cow, .turkey, .dog, .chicken, .cow, .turkey, .dog])
>> let payload: Data = try JSONEncoder().encode(farm)
>> 
>> 
>> 
>> do {
>> 
>> 
>> let farm = try JSONDecoder().decode(Farm.self, from: payload)
>> 
>> 
>> 
>> // Extracted as user types:
>> 
>> 
>> let coordinates = "\(farm.location.latitude, farm.location.longitude)"
>> } catch {
>> 
>> 
>> // Encountered error during deserialization
>> }
>> This gives developers access to their data in a type-safe manner and a 
>> recognizable interface.
>> 
>> Detailed design
>> To support user types, we expose the Codable protocol:
>> 
>> /// Conformance to `Codable` indicates that a type can marshal itself into 
>> and out of an external representation.
>> public protocol Codable {
>> 
>> 
>> /// Initializes `self` by decoding from `decoder`.
>> 
>> 
>> ///
>> 
>> 
>> /// - parameter decoder: The decoder to read data from.
>> 
>> 
>> /// - throws: An error if reading from the decoder fails, or if read data is 
>> corrupted or otherwise invalid.
>> 
>> 
>> init(from decoder: Decoder) throws
>> 
>> 
>> 
>> /// Encodes `self` into the given encoder.
>> 
>> 
>> ///
>> 
>> 
>> /// If `self` fails to encode anything, `encoder` will encode an empty 
>> `.default` container in its place.
>> 
>> 
>> ///
>> 
>> 
>> /// - parameter encoder: The encoder to write data to.
>> 
>> 
>> /// - throws: An error if any values are invalid for `encoder`'s format.
>> 
>> 
>> func encode(to encoder: Encoder) throws
>> }
>> By adopting Codable, user types opt in to this archival system.
>> 
>> Structured types (i.e. types which encode as a collection of properties) 
>> encode and decode their properties in a keyed manner. Keys may be 
>> String-convertible or Int-convertible (or both), and user types which have 
>> properties should declare semantic key enums which map keys to their 
>> properties. Keys must conform to the CodingKey protocol:
>> 
>> /// Conformance to `CodingKey` indicates that a type can be used as a key 
>> for encoding and decoding.
>> public protocol CodingKey {
>> 
>> 
>> /// The string to use in a named collection (e.g. a string-keyed dictionary).
>> 
>> 
>> var stringValue: String? { get }
>> 
>> 
>> 
>> /// Initializes `self` from a string.
>> 
>> 
>> ///
>> 
>> 
>> /// - parameter stringValue: The string value of the desired key.
>> 
>> 
>> /// - returns: An instance of `Self` from the given string, or `nil` if the 
>> given string does not correspond to any instance of `Self`.
>> 
>> 
>> init?(stringValue: String)
>> 
>> 
>> 
>> /// The int to use in an indexed collection (e.g. an int-keyed dictionary).
>> 
>> 
>> var intValue: Int? { get }
>> 
>> 
>> 
>> /// Initializes `self` from an integer.
>> 
>> 
>> ///
>> 
>> 
>> /// - parameter intValue: The integer value of the desired key.
>> 
>> 
>> /// - returns: An instance of `Self` from the given integer, or `nil` if the 
>> given integer does not correspond to any instance of `Self`.
>> 
>> 
>> init?(intValue: Int)
>> }
>> For most types, String-convertible keys are a reasonable default; for 
>> performance, however, Int-convertible keys are preferred, and Encoders may 
>> choose to make use of Ints over Strings. Framework types should provide keys 
>> which have both for flexibility and performance across different types of 
>> Encoders. It is generally an error to provide a key which has neither a 
>> stringValue nor an intValue.
>> 
>> By default, CodingKey conformance can be derived for enums which have either 
>> String or Int backing:
>> 
>> enum Keys1 : CodingKey {
>> 
>> 
>> case a // (stringValue: "a", intValue: nil)
>> 
>> 
>> case b // (stringValue: "b", intValue: nil)
>> }
>> 
>> 
>> 
>> enum Keys2 : String, CodingKey {
>> 
>> 
>> case c = "foo" // (stringValue: "foo", intValue: nil)
>> 
>> 
>> case d         // (stringValue: "d", intValue: nil)
>> }
>> 
>> 
>> 
>> enum Keys3 : Int, CodingKey {
>> 
>> 
>> case e = 4 // (stringValue: "e", intValue: 4)
>> 
>> 
>> case f     // (stringValue: "f", intValue: 5)
>> 
>> 
>> case g = 9 // (stringValue: "g", intValue: 9)
>> }
>> Coding keys which are not enums, have associated values, or have other raw 
>> representations must implement these methods manually.
>> 
>> In addition to automatic CodingKey conformance derivation for enums, 
>> Codableconformance can be automatically derived for certain types as well:
>> 
>>    • Types whose properties are all either Codable or primitive get an 
>> automatically derived String-backed CodingKeys enum mapping properties to 
>> case names
>>    • Types falling into (1) and types which provide a CodingKeys enum 
>> (directly or via a typealias) whose case names map to properties which are 
>> all Codableget automatic derivation of init(from:) and encode(to:) using 
>> those properties and keys. Types may choose to provide a custom init(from:) 
>> or encode(to:) (or both); whichever they do not provide will be 
>> automatically derived
>>    • Types which fall into neither (1) nor (2) will have to provide a custom 
>> key type and provide their own init(from:) and encode(to:)
>> Many types will either allow for automatic derivation of all codability (1), 
>> or provide a custom key subset and take advantage of automatic method 
>> derivation (2).
>> 
>> Encoding and Decoding
>> 
>> Types which are encodable encode their data into a container provided by 
>> their Encoder:
>> 
>> /// An `Encoder` is a type which can encode values into a native format for 
>> external representation.
>> public protocol Encoder {
>> 
>> 
>> /// Populates `self` with an encoding container (of `.default` type) and 
>> returns it, keyed by the given key type.
>> 
>> 
>> ///
>> 
>> 
>> /// - parameter type: The key type to use for the container.
>> 
>> 
>> /// - returns: A new keyed encoding container.
>> 
>> 
>> /// - precondition: May not be called after a previous 
>> `self.container(keyedBy:)` call of a different `EncodingContainerType`.
>> 
>> 
>> /// - precondition: May not be called after a value has been encoded through 
>> a prior `self.singleValueContainer()` call.
>> 
>> 
>> func container<Key : CodingKey>(keyedBy type: Key.Type) -> 
>> KeyedEncodingContainer<Key>
>> 
>> 
>> 
>> /// Returns an encoding container appropriate for holding a single primitive 
>> value.
>> 
>> 
>> ///
>> 
>> 
>> /// - returns: A new empty single value container.
>> 
>> 
>> /// - precondition: May not be called after a prior 
>> `self.container(keyedBy:)` call.
>> 
>> 
>> /// - precondition: May not be called after a value has been encoded through 
>> a previous `self.singleValueContainer()` call.
>> 
>> 
>> func singleValueContainer() -> SingleValueEncodingContainer
>> 
>> 
>> 
>> /// The path of coding keys taken to get to this point in encoding.
>> 
>> 
>> var codingKeyContext: [CodingKey] { get }
>> }
>> 
>> 
>> 
>> // Continuing examples from before; below is automatically generated by the 
>> compiler if no customization is needed.
>> public struct Location : Codable {
>> 
>> 
>> private enum CodingKeys : CodingKey {
>> 
>> 
>> case
>> latitutude
>> 
>> case
>> longitude
>> 
>> }
>> 
>> 
>> 
>> public func encode(to encoder: Encoder) throws {
>> 
>> 
>> // Generic keyed encoder gives type-safe key access: cannot encode with keys 
>> of the wrong type.
>> 
>> 
>> let container = encoder.container(keyedBy: CodingKeys.self)
>> 
>> 
>> 
>> // The encoder is generic on the key -- free key autocompletion here.
>> 
>> 
>> try container.encode(latitude, forKey: .latitude)
>> 
>> 
>> try container.encode(longitude, forKey: .longitude)
>> 
>> 
>> }
>> }
>> 
>> 
>> 
>> public struct Farm : Codable {
>> 
>> 
>> private enum CodingKeys : CodingKey {
>> 
>> 
>> case
>> name
>> 
>> case
>> location
>> 
>> case
>> animals
>> 
>> }
>> 
>> 
>> 
>> public func encode(to encoder: Encoder) throws {
>> 
>> 
>> let container = encoder.container(keyedBy: CodingKeys.self)
>> 
>> 
>> try container.encode(name, forKey: .name)
>> 
>> 
>> try container.encode(location, forKey: .location)
>> 
>> 
>> try container.encode(animals, forKey: .animals)
>> 
>> 
>> }
>> }
>> Similarly, decodable types initialize from data read from their Decoder's 
>> container:
>> 
>> /// A `Decoder` is a type which can decode values from a native format into 
>> in-memory representations.
>> public protocol Decoder {
>> 
>> 
>> /// Returns the data stored in `self` as represented in a container keyed by 
>> the given key type.
>> 
>> 
>> ///
>> 
>> 
>> /// - parameter type: The key type to use for the container.
>> 
>> 
>> /// - returns: A keyed decoding container view into `self`.
>> 
>> 
>> /// - throws: `CocoaError.coderTypeMismatch` if the encountered stored value 
>> is not a keyed container.
>> 
>> 
>> func container<Key : CodingKey>(keyedBy type: Key.Type) throws -> 
>> KeyedDecodingContainer<Key>
>> 
>> 
>> 
>> /// Returns the data stored in `self` as represented in a container 
>> appropriate for holding a single primitive value.
>> 
>> 
>> ///
>> 
>> 
>> /// - returns: A single value container view into `self`.
>> 
>> 
>> /// - throws: `CocoaError.coderTypeMismatch` if the encountered stored value 
>> is not a single value container.
>> 
>> 
>> func singleValueContainer() throws -> SingleValueDecodingContainer
>> 
>> 
>> 
>> /// The path of coding keys taken to get to this point in decoding.
>> 
>> 
>> var codingKeyContext: [CodingKey] { get }
>> }
>> 
>> 
>> 
>> // Continuing examples from before; below is automatically generated by the 
>> compiler if no customization is needed.
>> public struct Location : Codable {
>> 
>> 
>> public init(from decoder: Decoder) throws {
>> 
>> 
>> let container = try decoder.container(keyedBy: CodingKeys.self)
>> 
>>        latitude 
>> = try container.decode(Double.self, forKey: .latitude)
>> 
>>        longitude 
>> = try container.decode(Double.self, forKey: .longitude)
>> 
>> 
>> }
>> }
>> 
>> 
>> 
>> public struct Farm : Codable {
>> 
>> 
>> public init(from decoder: Decoder) throws {
>> 
>> 
>> let container = try decoder.container(keyedBy: CodingKeys.self)
>> 
>>        name 
>> = try container.decode(String.self, forKey: .name)
>> 
>>        location 
>> = try container.decode(Location.self, forKey: .location)
>> 
>>        animals 
>> = try container.decode([Animal].self, forKey: .animals)
>> 
>> 
>> }
>> }
>> Keyed Encoding Containers
>> 
>> Keyed encoding containers are the primary interface that most Codable types 
>> interact with for encoding and decoding. Through these, Codable types have 
>> strongly-keyed access to encoded data by using keys that are semantically 
>> correct for the operations they want to express.
>> 
>> Since semantically incompatible keys will rarely (if ever) share the same 
>> key type, it is impossible to mix up key types within the same container (as 
>> is possible with Stringkeys), and since the type is known statically, keys 
>> get autocompletion by the compiler.
>> 
>> /// `KeyedEncodingContainer` is a generic abstract base class that provides 
>> a view into an `Encoders` storage and is used to hold the encoded properties 
>> of a `Codable` type.
>> ///
>> /// Encoders should provide subclasses of `KeyedEncodingContainer` for their 
>> format.
>> 
>> open 
>> class KeyedEncodingContainer<Key : CodingKey> {
>> 
>> 
>> /// Encodes the given value for the given key.
>> 
>> 
>> ///
>> 
>> 
>> /// - parameter value: The value to encode.
>> 
>> 
>> /// - parameter key: The key to associate the value with.
>> 
>> 
>> /// - throws: `CocoaError.coderInvalidValue` if the given value is invalid 
>> in the current context for this format.
>> 
>> 
>> /// - precondition: The key must have a `stringValue` or `intValue` 
>> appropriate for the encoding container type.
>> 
>>    open 
>> func encode<Value : Codable>(_ value: Value?, forKey key: Key) throws
>> 
>> 
>> 
>> /// Encodes the given value for the given key.
>> 
>> 
>> ///
>> 
>> 
>> /// - parameter value: The value to encode.
>> 
>> 
>> /// - parameter key: The key to associate the value with.
>> 
>> 
>> /// - throws: `CocoaError.coderInvalidValue` if the given value is invalid 
>> in the current context for this format.
>> 
>> 
>> /// - precondition: The key must have a `stringValue` or `intValue` 
>> appropriate for the encoding container type.
>> 
>>    open 
>> func encode(_ value: Bool?,   forKey key: Key) throws
>> 
>>    open 
>> func encode(_ value: Int?,    forKey key: Key) throws
>> 
>>    open 
>> func encode(_ value: Int8?,   forKey key: Key) throws
>> 
>>    open 
>> func encode(_ value: Int16?,  forKey key: Key) throws
>> 
>>    open 
>> func encode(_ value: Int32?,  forKey key: Key) throws
>> 
>>    open 
>> func encode(_ value: Int64?,  forKey key: Key) throws
>> 
>>    open 
>> func encode(_ value: UInt?,   forKey key: Key) throws
>> 
>>    open 
>> func encode(_ value: UInt8?,  forKey key: Key) throws
>> 
>>    open 
>> func encode(_ value: UInt16?, forKey key: Key) throws
>> 
>>    open 
>> func encode(_ value: UInt32?, forKey key: Key) throws
>> 
>>    open 
>> func encode(_ value: UInt64?, forKey key: Key) throws
>> 
>>    open 
>> func encode(_ value: Float?,  forKey key: Key) throws
>> 
>>    open 
>> func encode(_ value: Double?, forKey key: Key) throws
>> 
>>    open 
>> func encode(_ value: String?, forKey key: Key) throws
>> 
>>    open 
>> func encode(_ value: Data?,   forKey key: Key) throws
>> 
>> 
>> 
>> /// Encodes the given object weakly for the given key.
>> 
>> 
>> ///
>> 
>> 
>> /// For `Encoder`s that implement this functionality, this will only encode 
>> the given object and associate it with the given key if it encoded 
>> unconditionally elsewhere in the archive (either previously or in the 
>> future).
>> 
>> 
>> ///
>> 
>> 
>> /// For formats which don't support this feature, the default implementation 
>> encodes the given object unconditionally.
>> 
>> 
>> ///
>> 
>> 
>> /// - parameter object: The object to encode.
>> 
>> 
>> /// - parameter key: The key to associate the object with.
>> 
>> 
>> /// - throws: `CocoaError.coderInvalidValue` if the given value is invalid 
>> in the current context for this format.
>> 
>> 
>> /// - precondition: The key must have a `stringValue` or `intValue` 
>> appropriate for the encoding container type.
>> 
>>    open 
>> func encodeWeak<Object : AnyObject & Codable>(_ object: Object?, forKey key: 
>> Key) throws
>> 
>> 
>> 
>> /// The path of coding keys taken to get to this point in encoding.
>> 
>>    open 
>> var codingKeyContext: [CodingKey]
>> }
>> 
>> 
>> 
>> /// `KeyedDecodingContainer` is a generic abstract base class that provides 
>> a view into an `Decoders` storage and is used to hold the encoded properties 
>> of a `Codable` type.
>> ///
>> /// Decoders should provide subclasses of `KeyedDecodingContainer` for their 
>> format.
>> 
>> open 
>> class KeyedDecodingContainer<Key : CodingKey> {
>> 
>> 
>> /// All the keys the `Decoder` has for this container.
>> 
>> 
>> ///
>> 
>> 
>> /// Different keyed containers from the same `Decoder` may return different 
>> keys here; it is possible to encode with multiple key types which are not 
>> convertible to one another. This should report all keys present which are 
>> convertible to the requested type.
>> 
>>    open 
>> var allKeys: [Key]
>> 
>> 
>> 
>> /// Returns whether the `Decoder` contains a value associated with the given 
>> key.
>> 
>> 
>> ///
>> 
>> 
>> /// The value associated with the given key may be a null value as 
>> appropriate for the data format.
>> 
>> 
>> ///
>> 
>> 
>> /// - parameter key: The key to search for.
>> 
>> 
>> /// - returns: Whether the `Decoder` has an entry for the given key.
>> 
>>    open 
>> func contains(_ key: Key) -> Bool
>> 
>> 
>> 
>> /// Decodes a value of the given type for the given key.
>> 
>> 
>> ///
>> 
>> 
>> /// A default implementation is given for these types which calls into the 
>> abstract `decodeIfPresent` implementations below.
>> 
>> 
>> ///
>> 
>> 
>> /// - parameter type: The type of value to decode.
>> 
>> 
>> /// - parameter key: The key that the decoded value is associated with.
>> 
>> 
>> /// - returns: A value of the requested type, if present for the given key 
>> and convertible to the requested type.
>> 
>> 
>> /// - throws: `CocoaError.coderTypeMismatch` if the encountered encoded 
>> value is not convertible to the requested type.
>> 
>> 
>> /// - throws: `CocoaError.coderValueNotFound` if `self` does not have an 
>> entry for the given key or if the value is null.
>> 
>>    open 
>> func decode(_ type: Bool.Type,   forKey key: Key) throws -> Bool
>> 
>>    open 
>> func decode(_ type: Int.Type,    forKey key: Key) throws -> Int
>> 
>>    open 
>> func decode(_ type: Int8.Type,   forKey key: Key) throws -> Int8
>> 
>>    open 
>> func decode(_ type: Int16.Type,  forKey key: Key) throws -> Int16
>> 
>>    open 
>> func decode(_ type: Int32.Type,  forKey key: Key) throws -> Int32
>> 
>>    open 
>> func decode(_ type: Int64.Type,  forKey key: Key) throws -> Int64
>> 
>>    open 
>> func decode(_ type: UInt.Type,   forKey key: Key) throws -> UInt
>> 
>>    open 
>> func decode(_ type: UInt8.Type,  forKey key: Key) throws -> UInt8
>> 
>>    open 
>> func decode(_ type: UInt16.Type, forKey key: Key) throws -> UInt16
>> 
>>    open 
>> func decode(_ type: UInt32.Type, forKey key: Key) throws -> UInt32
>> 
>>    open 
>> func decode(_ type: UInt64.Type, forKey key: Key) throws -> UInt64
>> 
>>    open 
>> func decode(_ type: Float.Type,  forKey key: Key) throws -> Float
>> 
>>    open 
>> func decode(_ type: Double.Type, forKey key: Key) throws -> Double
>> 
>>    open 
>> func decode(_ type: String.Type, forKey key: Key) throws -> String
>> 
>>    open 
>> func decode(_ type: Data.Type,   forKey key: Key) throws -> Data
>> 
>>    open 
>> func decode<Value : Codable>(_ type: Value.Type, forKey key: Key) throws -> 
>> Value
>> 
>> 
>> 
>> /// Decodes a value of the given type for the given key, if present.
>> 
>> 
>> ///
>> 
>> 
>> /// This method returns `nil` if the container does not have a value 
>> associated with `key`, or if the value is null. The difference between these 
>> states can be disambiguated with a `contains(_:)` call.
>> 
>> 
>> ///
>> 
>> 
>> /// - parameter type: The type of value to decode.
>> 
>> 
>> /// - parameter key: The key that the decoded value is associated with.
>> 
>> 
>> /// - returns: A decoded value of the requested type, or `nil` if the 
>> `Decoder` does not have an entry associated with the given key, or if the 
>> value is a null value.
>> 
>> 
>> /// - throws: `CocoaError.coderTypeMismatch` if the encountered encoded 
>> value is not convertible to the requested type.
>> 
>>    open 
>> func decodeIfPresent(_ type: Bool.Type,   forKey key: Key) throws -> Bool?
>> 
>>    open 
>> func decodeIfPresent(_ type: Int.Type,    forKey key: Key) throws -> Int?
>> 
>>    open 
>> func decodeIfPresent(_ type: Int8.Type,   forKey key: Key) throws -> Int8?
>> 
>>    open 
>> func decodeIfPresent(_ type: Int16.Type,  forKey key: Key) throws -> Int16?
>> 
>>    open 
>> func decodeIfPresent(_ type: Int32.Type,  forKey key: Key) throws -> Int32?
>> 
>>    open 
>> func decodeIfPresent(_ type: Int64.Type,  forKey key: Key) throws -> Int64?
>> 
>>    open 
>> func decodeIfPresent(_ type: UInt.Type,   forKey key: Key) throws -> UInt?
>> 
>>    open 
>> func decodeIfPresent(_ type: UInt8.Type,  forKey key: Key) throws -> UInt8?
>> 
>>    open 
>> func decodeIfPresent(_ type: UInt16.Type, forKey key: Key) throws -> UInt16?
>> 
>>    open 
>> func decodeIfPresent(_ type: UInt32.Type, forKey key: Key) throws -> UInt32?
>> 
>>    open 
>> func decodeIfPresent(_ type: UInt64.Type, forKey key: Key) throws -> UInt64?
>> 
>>    open 
>> func decodeIfPresent(_ type: Float.Type,  forKey key: Key) throws -> Float?
>> 
>>    open 
>> func decodeIfPresent(_ type: Double.Type, forKey key: Key) throws -> Double?
>> 
>>    open 
>> func decodeIfPresent(_ type: String.Type, forKey key: Key) throws -> String?
>> 
>>    open 
>> func decodeIfPresent(_ type: Data.Type,   forKey key: Key) throws -> Data?
>> 
>>    open 
>> func decodeIfPresent<Value : Codable>(_ type: Value.Type, forKey key: Key) 
>> throws -> Value?
>> 
>> 
>> 
>> /// The path of coding keys taken to get to this point in decoding.
>> 
>>    open 
>> var codingKeyContext: [CodingKey]
>> }
>> These encode(_:forKey:) and decode(_:forKey:) overloads give strong, static 
>> type guarantees about what is encodable (preventing accidental attempts to 
>> encode an invalid type), and provide a list of primitive types which are 
>> common to all encoders and decoders that users can rely on.
>> 
>> Coming in Swift 4 is the ability to express that "a collection of things 
>> which are Codable is Codable" (conditional conformance), allowing 
>> collections which we extend (Array, Dictionary, etc.) to fall into these 
>> overloads as well.
>> 
>> Encoding Container Types
>> 
>> For some types, the container into which they encode has meaning. Especially 
>> when coding for a specific output format (e.g. when communicating with a 
>> JSON API), a type may wish to explicitly encode as an array or a dictionary:
>> 
>> // Continuing from before
>> public protocol Encoder {
>> 
>> 
>> /// Populates `self` with an encoding container of the given type and 
>> returns it, keyed by the given key type.
>> 
>> 
>> ///
>> 
>> 
>> /// A default implementation of `Encoder.container(keyedBy:)` calls this 
>> method with a container type of `.default`.
>> 
>> 
>> ///
>> 
>> 
>> /// - parameter keyType: The key type to use for the container.
>> 
>> 
>> /// - parameter containerType: The container type to create.
>> 
>> 
>> /// - returns: A new keyed encoding container.
>> 
>> 
>> /// - precondition: May not be called after a previous 
>> `self.container(keyedBy:)` call of a different `EncodingContainerType`.
>> 
>> 
>> /// - precondition: May not be called after a value has been encoded through 
>> a prior `self.singleValueContainer()` call.
>> 
>> 
>> func container<Key : CodingKey>(keyedBy keyType: Key.Type, type 
>> containerType: EncodingContainerType) -> KeyedEncodingContainer<Key>
>> }
>> 
>> 
>> 
>> /// An `EncodingContainerType` specifies the type of container an `Encoder` 
>> should use to store values.
>> public enum EncodingContainerType {
>> 
>> 
>> /// The `Encoder`'s preferred container type; equivalent to either `.array` 
>> or `.dictionary` as appropriate for the encoder.
>> 
>> 
>> case `default
>> `
>> 
>> 
>> /// Explicitly requests the use of an array to store encoded values.
>> 
>> 
>> case
>> array
>> 
>> 
>> /// Explicitly requests the use of a dictionary to store encoded values.
>> 
>> 
>> case
>> dictionary
>> 
>> }
>> Single Value Containers
>> 
>> For other types, an array or dictionary container may not even make sense 
>> (e.g. values which are RawRepresentable as a single primitive value). Those 
>> types may encode and decode directly as a single value, instead of 
>> requesting a keyed container:
>> 
>> /// A `SingleValueEncodingContainer` is a container which can support the 
>> storage and direct encoding of a single non-keyed value.
>> public protocol SingleValueEncodingContainer {
>> 
>> 
>> /// Encodes a single value of the given type.
>> 
>> 
>> ///
>> 
>> 
>> /// - parameter value: The value to encode.
>> 
>> 
>> /// - throws: `CocoaError.coderInvalidValue` if the given value is invalid 
>> in the current context for this format.
>> 
>> 
>> /// - precondition: May not be called after a previous `self.encode(_:)` 
>> call.
>> 
>> 
>> func encode(_ value: Bool) throws
>> 
>> 
>> func encode(_ value: Int) throws
>> 
>> 
>> func encode(_ value: Int8) throws
>> 
>> 
>> func encode(_ value: Int16) throws
>> 
>> 
>> func encode(_ value: Int32) throws
>> 
>> 
>> func encode(_ value: Int64) throws
>> 
>> 
>> func encode(_ value: UInt) throws
>> 
>> 
>> func encode(_ value: UInt8) throws
>> 
>> 
>> func encode(_ value: UInt16) throws
>> 
>> 
>> func encode(_ value: UInt32) throws
>> 
>> 
>> func encode(_ value: UInt64) throws
>> 
>> 
>> func encode(_ value: Float) throws
>> 
>> 
>> func encode(_ value: Double) throws
>> 
>> 
>> func encode(_ value: String) throws
>> 
>> 
>> func encode(_ value: Data) throws
>> }
>> 
>> 
>> 
>> /// A `SingleValueDecodingContainer` is a container which can support the 
>> storage and direct decoding of a single non-keyed value.
>> public protocol SingleValueDecodingContainer {
>> 
>> 
>> /// Decodes a single value of the given type.
>> 
>> 
>> ///
>> 
>> 
>> /// - parameter type: The type to decode as.
>> 
>> 
>> /// - returns: A value of the requested type.
>> 
>> 
>> /// - throws: `CocoaError.coderTypeMismatch` if the encountered encoded 
>> value cannot be converted to the requested type.
>> 
>> 
>> func decode(_ type: Bool.Type) throws -> Bool
>> 
>> 
>> func decode(_ type: Int.Type) throws -> Int
>> 
>> 
>> func decode(_ type: Int8.Type) throws -> Int8
>> 
>> 
>> func decode(_ type: Int16.Type) throws -> Int16
>> 
>> 
>> func decode(_ type: Int32.Type) throws -> Int32
>> 
>> 
>> func decode(_ type: Int64.Type) throws -> Int64
>> 
>> 
>> func decode(_ type: UInt.Type) throws -> UInt
>> 
>> 
>> func decode(_ type: UInt8.Type) throws -> UInt8
>> 
>> 
>> func decode(_ type: UInt16.Type) throws -> UInt16
>> 
>> 
>> func decode(_ type: UInt32.Type) throws -> UInt32
>> 
>> 
>> func decode(_ type: UInt64.Type) throws -> UInt64
>> 
>> 
>> func decode(_ type: Float.Type) throws -> Float
>> 
>> 
>> func decode(_ type: Double.Type) throws -> Double
>> 
>> 
>> func decode(_ type: String.Type) throws -> String
>> 
>> 
>> func decode(_ type: Data.Type) throws -> Data
>> }
>> 
>> 
>> 
>> // Continuing example from before; below is automatically generated by the 
>> compiler if no customization is needed.
>> public enum Animal : Int, Codable {
>> 
>> 
>> public func encode(to encoder: Encoder) throws {
>> 
>> 
>> // Encode as a single value; no keys.
>> 
>> 
>> try encoder.singleValueContainer.encode(self.rawValue)
>> 
>> 
>> }
>> 
>> 
>> 
>> public init(from decoder: Decoder) throws {
>> 
>> 
>> // Decodes as a single value; no keys.
>> 
>> 
>> let intValue = try decoder.singleValueContainer().decode(Int.self)
>> 
>> 
>> if let value = Self(rawValue: intValue) {
>> 
>> 
>> self =
>> value
>> 
>> } else {
>> 
>> 
>> throw CocoaError.error(.coderReadCorrupt)
>> 
>> 
>> }
>> 
>> 
>> }
>> }
>> In the example given above, since Animal uses a single value container, 
>> [.chicken, .dog, .cow, .turkey, .dog, .chicken, .cow, .turkey, .dog]would 
>> encode directly as [1, 2, 4, 3, 2, 1, 4, 3, 2].
>> 
>> Nesting
>> 
>> In practice, some types may also need to control how data is nested within 
>> their container, or potentially nest other containers within their 
>> container. Keyed containers allow this by returning nested containers of 
>> differing key types:
>> 
>> // Continuing from before
>> 
>> open 
>> class KeyedEncodingContainer<Key : CodingKey> {
>> 
>> 
>> /// Stores an encoding container for the given key and returns it.
>> 
>> 
>> ///
>> 
>> 
>> /// - parameter keyType: The key type to use for the container.
>> 
>> 
>> /// - parameter containerType: The container type to create.
>> 
>> 
>> /// - parameter key: The key to encode the container for.
>> 
>> 
>> /// - returns: A new keyed encoding container.
>> 
>>    open 
>> func nestedContainer<NestedKey : CodingKey>(keyedBy keyType: NestedKey.Type, 
>> type containerType: EncodingContainerType, forKey key: Key) -> 
>> KeyedEncodingContainer<NestedKey>
>> }
>> 
>> 
>> open 
>> class KeyedDecodingContainer<Key : CodingKey> {
>> 
>> 
>> /// Returns the data stored for the given key as represented in a container 
>> keyed by the given key type.
>> 
>> 
>> ///
>> 
>> 
>> /// - parameter type: The key type to use for the container.
>> 
>> 
>> /// - parameter key: The key that the nested container is associated with.
>> 
>> 
>> /// - returns: A keyed decoding container view into `self`.
>> 
>> 
>> /// - throws: `CocoaError.coderTypeMismatch` if the encountered stored value 
>> is not a container.
>> 
>>    open 
>> func nestedContainer<NestedKey : CodingKey>(keyedBy type: NestedKey.Type, 
>> forKey key: Key) throws -> KeyedDecodingContainer<NestedKey>
>> }
>> This can be common when coding against specific external data 
>> representations:
>> 
>> // User type for interfacing with a specific JSON API. JSON API expects 
>> encoding as {"id": ..., "properties": {"name": ..., "timestamp": ...}}. 
>> Swift type differs from encoded type, and encoding needs to match a spec:
>> struct Record : Codable {
>> 
>> 
>> // We care only about these values from the JSON payload
>> 
>> 
>> let id: Int
>> 
>> 
>> let name: String
>> 
>> 
>> let timestamp: Double
>> 
>> 
>> 
>> // ...
>> 
>> 
>> 
>> private enum Keys : CodingKey {
>> 
>> 
>> case
>> id
>> 
>> case
>> properties
>> 
>> }
>> 
>> 
>> 
>> private enum PropertiesKeys : CodingKey {
>> 
>> 
>> case
>> name
>> 
>> case
>> timestamp
>> 
>> }
>> 
>> 
>> 
>> public func encode(to encoder: Encoder) throws {
>> 
>> 
>> let container = encoder.container(keyedBy: Keys.self, type: .dictionary)
>> 
>> 
>> try container.encode(id, forKey: .id)
>> 
>> 
>> 
>> // Set a dictionary for the "properties" key
>> 
>> 
>> let nested = container.nestedContainer(keyedBy: PropertiesKeys.self, type: 
>> .dictionary, forKey: .properties)
>> 
>> 
>> try nested.encode(name, forKey: .name)
>> 
>> 
>> try nested.encode(timestamp, forKey: .timestamp)
>> 
>> 
>> }
>> 
>> 
>> 
>> public init(from decoder: Decoder) throws {
>> 
>> 
>> let container = try decoder.container(keyedBy: Keys.self)
>> 
>>        id 
>> = try container.decode(Int.self, forKey: .id)
>> 
>> 
>> 
>> let nested = try container.nestedContainer(keyedBy: PropertiesKeys.self, 
>> forKey: .properties)
>> 
>>        name 
>> = try nested.decode(String.self, forKey: .name)
>> 
>>        timestamp 
>> = try nested.decode(Double.self, forKey: .timestamp)
>> 
>> 
>> }
>> }
>> Inheritance
>> 
>> Inheritance in this system is supported much like it is with NSCoding — on 
>> encoding, objects which inherit from a type that is Codable encode super 
>> using their encoder, and pass a decoder to super.init(from:) on decode. With 
>> the existing NSCoding API, this is most often done like so, by convention:
>> 
>> - (void)encodeWithCoder:(NSCoder *)encoder {
>> 
>> 
>> [super encodeWithCoder:encoder];
>> 
>> 
>> // ... encode properties
>> }
>> 
>> 
>> 
>> - (instancetype)initWithCoder:(NSCoder *)decoder {
>> 
>> 
>> if ((self = [super initWithCoder:decoder])) {
>> 
>> 
>> // ... decode properties
>> 
>> 
>> }
>> 
>> 
>> 
>> return self;
>> }
>> In practice, this approach means that the properties of self and the 
>> properties of super get encoded into the same container: if self encodes 
>> values for keys "a", "b", and "c", and super encodes "d", "e", and "f", the 
>> resulting object is archived as {"a": ..., "b": ..., "c": ..., "d": ..., 
>> "e": ..., "f": ...}. This approach has two drawbacks:
>> 
>>    • Things which self encodes may overwrite super's (or vice versa, 
>> depending on when -[super encodeWithCoder:] is called
>>    • self and super may not encode into different container types (e.g. self 
>> in a sequential fashion, and super in a keyed fashion)
>> The second point is not an issue for NSKeyedArchiver, since all values 
>> encode with keys (sequentially coded elements get autogenerated keys). This 
>> proposed API, however, allows for self and super to explicitly request 
>> conflicting containers (.arrayand .dictionary, which may not be mixed, 
>> depending on the data format).
>> 
>> To remedy both of these points, we adopt a new convention for 
>> inheritance-based coding — encoding super as a sub-object of self:
>> 
>> public class MyCodable : SomethingCodable {
>> 
>> 
>> public func encode(to encoder: Encoder) throws {
>> 
>> 
>> let container = encoder.container(keyedBy: CodingKeys.self)
>> 
>> 
>> // ... encode some properties
>> 
>> 
>> 
>> // superEncoder() gives `super` a nested container to encode into (for
>> 
>> 
>> // a predefined key).
>> 
>> 
>> try super.encode(to: container.superEncoder())
>> 
>> 
>> }
>> 
>> 
>> 
>> public init(from decoder: Decoder) throws {
>> 
>> 
>> let container = try decoder.container(keyedBy: CodingKeys.self)
>> 
>> 
>> // ... decode some properties
>> 
>> 
>> 
>> // Allow `super` to decode from the nested container.
>> 
>> 
>> try super.init(from: container.superDecoder())
>> 
>> 
>> }
>> }
>> If a shared container is desired, it is still possible to call 
>> super.encode(to: encoder) and super.init(from: decoder), but we recommend 
>> the safer containerized option.
>> 
>> superEncoder() and superDecoder() are provided on KeyedEncodingContainer and 
>> KeyedDecodingContainer to provide handles to containers for super to use. 
>> While users may specify a custom key to encode super with, the default 
>> behavior is to use a key with a stringValue of "super" and an intValue of 0:
>> 
>> // Continuing from before
>> 
>> open 
>> class KeyedEncodingContainer<Key : CodingKey> {
>> 
>> 
>> /// Stores a new nested container for the default `super` key and returns a 
>> new `Encoder` instance for encoding `super` into that container.
>> 
>> 
>> ///
>> 
>> 
>> /// Equivalent to calling `superEncoder(forKey:)` with `Key(stringValue: 
>> "super", intValue: 0)`.
>> 
>> 
>> ///
>> 
>> 
>> /// - returns: A new `Encoder` to pass to `super.encode(to:)`.
>> 
>>    open 
>> func superEncoder() -> Encoder
>> 
>> 
>> 
>> /// Stores a new nested container for the given key and returns a new 
>> `Encoder` instance for encoding `super` into that container.
>> 
>> 
>> ///
>> 
>> 
>> /// - parameter key: The key to encode `super` for.
>> 
>> 
>> /// - returns: A new `Encoder` to pass to `super.encode(to:)`.
>> 
>> 
>> /// - precondition: The key must have a `stringValue` or `intValue` 
>> appropriate for the encoding container type.
>> 
>>    open 
>> func superEncoder(forKey key: Key) -> Encoder
>> }
>> 
>> 
>> open 
>> class KeyedDecodingContainer<Key : CodingKey> {
>> 
>> 
>> /// Returns a `Decoder` instance for decoding `super` from the container 
>> associated with the default `super` key.
>> 
>> 
>> ///
>> 
>> 
>> /// Equivalent to calling `superDecoder(forKey:)` with `Key(stringValue: 
>> "super", intValue: 0)`.
>> 
>> 
>> ///
>> 
>> 
>> /// - returns: A new `Decoder` to pass to `super.init(from:)`.
>> 
>> 
>> /// - throws: `CocoaError.coderValueNotFound` if `self` does not have an 
>> entry for the default `super` key, or if the stored value is null.
>> 
>>    open 
>> func superDecoder() throws -> Decoder
>> 
>> 
>> 
>> /// Returns a `Decoder` instance for decoding `super` from the container 
>> associated with the given key.
>> 
>> 
>> ///
>> 
>> 
>> /// - parameter key: The key to decode `super` for.
>> 
>> 
>> /// - returns: A new `Decoder` to pass to `super.init(from:)`.
>> 
>> 
>> /// - throws: `CocoaError.coderValueNotFound` if `self` does not have an 
>> entry for the given key, or if the stored value is null.
>> 
>>    open 
>> func superDecoder(forKey key: Key) throws -> Decoder
>> }
>> Primitive Codable Conformance
>> 
>> The encoding container types offer overloads for working with and processing 
>> the API's primitive types (String, Int, Double, etc.). However, for ease of 
>> implementation (both in this API and others), it can be helpful for these 
>> types to conform to Codable themselves. Thus, along with these overloads, we 
>> will offer Codableconformance on these types:
>> 
>> extension Bool : Codable {
>> 
>> 
>> public init(from decoder: Decoder) throws {
>> 
>> 
>> self = try decoder.singleValueContainer().decode(Bool.self)
>> 
>> 
>> }
>> 
>> 
>> 
>> public func encode(to encoder: Encoder) throws {
>> 
>> 
>> try encoder.singleValueContainer().encode( self)
>> 
>> 
>> }
>> }
>> 
>> 
>> 
>> // Repeat for others...
>> This conformance allows one to write functions which accept Codable types 
>> without needing specific overloads for the fifteen primitive types as well. 
>> This also simplifies conditional conformance (e.g. expressing "extension 
>> Array : Codable where Element : Codable") by removing the need for 
>> additional explicit conformances for these types.
>> 
>> Since Swift's function overload rules prefer more specific functions over 
>> generic functions, the specific overloads are chosen where possible (e.g. 
>> encode("Hello, world!", forKey: .greeting) will choose encode(_: String, 
>> forKey: Key) over encode<T : Codable>(_: T, forKey: Key)). This maintains 
>> performance over dispatching through the Codable existential, while allowing 
>> for the flexibility of fewer overloads where applicable.
>> 
>> Additional Extensions
>> 
>> Along with the primitive Codable conformance above, extensions on 
>> CodableRawRepresentable types whose RawValue is a primitive types will 
>> provide default implementations for encoding and decoding:
>> 
>> public extension RawRepresentable where RawValue == Bool, Self : Codable {
>> 
>> 
>> public init(from decoder: Decoder) throws {
>> 
>> 
>> let decoded = try decoder.singleValueContainer().decode(RawValue.self)
>> 
>> 
>> guard let value = Self(rawValue: decoded) else {
>> 
>> 
>> throw CocoaError.error(.coderReadCorrupt)
>> 
>> 
>> }
>> 
>> 
>> 
>> self =
>> value
>> 
>> }
>> 
>> 
>> 
>> public func encode(to encoder: Encoder) throws {
>> 
>> 
>> try encoder.singleValueContainer().encode(self.rawValue)
>> 
>> 
>> }
>> }
>> 
>> 
>> 
>> // Repeat for others...
>> This allows for trivial Codable conformance of enum types (and manual 
>> RawRepresentable implementations) with primitive backing.
>> 
>> Source compatibility
>> This proposal is additive — existing code will not have to change due to 
>> this API addition. This implementation can be made available in both Swift 4 
>> and the Swift 3 compatibility mode.
>> 
>> Effect on ABI stability
>> The addition of this API will not be an ABI-breaking change. However, this 
>> will add limitations for changes in future versions of Swift, as parts of 
>> the API will have to remain unchanged between versions of Swift (barring 
>> some additions, discussed below).
>> 
>> Effect on API resilience
>> Much like new API added to the standard library, once added, many changes to 
>> this API will be ABI- and source-breaking changes. In particular, changes 
>> which change the types or names of methods or arguments, add required 
>> methods on protocols or classes, or remove supplied default implementations 
>> will break client behavior.
>> 
>> The following protocols and classes may not have methods added to them 
>> without providing default implementations:
>> 
>>    • Codable
>>    • CodingKey
>>    • Encoder
>>    • SingleValueEncodingContainer
>>    • KeyedEncodingContainer
>>    • Decoder
>>    • SingleValueDecodingContainer
>>    • KeyedDecodingContainer
>> The following classes may not remove existing default implementations:
>> 
>>    • KeyedEncodingContainer
>>    • KeyedDecodingContainer
>> Various extensions to Swift primitive types (Bool, Int, Double, etc.) and to 
>> RawRepresentable types (where RawValue == Bool, == Int, == Double, etc.) may 
>> also not be removed.
>> 
>> In general, changes to the proposed types will be restricted as described in 
>> the library evolution document in the Swift repository.
>> 
>> _______________________________________________
>> swift-evolution mailing list
>> [email protected]
>> https://lists.swift.org/mailman/listinfo/swift-evolution
> 
> _______________________________________________
> swift-evolution mailing list
> [email protected]
> https://lists.swift.org/mailman/listinfo/swift-evolution
_______________________________________________
swift-evolution mailing list
[email protected]
https://lists.swift.org/mailman/listinfo/swift-evolution

Reply via email to