-
Notifications
You must be signed in to change notification settings - Fork 2.4k
/
0166-swift-archival-serialization.md
2205 lines (1846 loc) · 113 KB
/
0166-swift-archival-serialization.md
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
# Swift Archival & Serialization
* Proposal: [SE-0166](0166-swift-archival-serialization.md)
* Authors: [Itai Ferber](https://github.com/itaiferber), [Michael LeHew](https://github.com/mlehew), [Tony Parker](https://github.com/parkera)
* Review Manager: [Doug Gregor](https://github.com/DougGregor)
* Status: **Implemented (Swift 4.0)**
* Decision Notes: [Rationale](https://forums.swift.org/t/accepted-se-0166-swift-archival-serialization/5780)
* Implementation: [apple/swift#9004](https://github.com/apple/swift/pull/9004)
## 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 `struct` types — `NSCoding` is an `@objc` protocol, conformance to which excludes non-`class` types. This 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 `enums` provide, 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:
1. This proposal describes the basis for this API, focusing on the protocols that users adopt and interface with
2. The next stage will propose specific API for new encoders
3. The final stage will discuss how this new API will interop with `NSCoding` as it is today
[SE-0167](0167-swift-encoders.md) provides stages 2 and 3.
## Proposed solution
We will be introducing the `Encodable` and `Decodable` protocols, adoption of which will allow end user types to participate in encoding and decoding:
```swift
// Codable implies Encodable and Decodable
// If all properties are Codable, protocol implementation is automatically generated by the compiler:
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-0167](0167-swift-encoders.md), not here) that take advantage of this conformance to offer type-safe serialization of user models:
```swift
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
We will be introducing the following new types to the Swift standard library:
* `protocol Encodable` & `protocol Decodable`: Adopted by types to opt into archival. Implementation can be synthesized by the compiler in cases where all properties are also `Encodable` or `Decodable`
* `protocol CodingKey`: Adopted by types used as keys for keyed containers, replacing `String` keys with semantic types. Implementation can be synthesized by the compiler in most cases
* `protocol Encoder`: Adopted by types which can take `Encodable` values and encode them into a native format
* `protocol KeyedEncodingContainerProtocol`: Adopted by types which provide a concrete way to store encoded values by `CodingKey`. Types adopting `Encoder` should provide types conforming to `KeyedEncodingContainerProtocol` to vend
* `struct KeyedEncodingContainer<Key : CodingKey>`: A concrete type-erased box for exposing `KeyedEncodingContainerProtocol` types; this is a type consumers of the API interact with directly
* `protocol UnkeyedEncodingContainer`: Adopted by types which provide a concrete way to stored encoded values with no keys. Types adopting `Encoder` should provide types conforming to `UnkeyedEncodingContainer` 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
* `protocol Decoder`: Adopted by types which can take payloads in a native format and decode `Decodable` values out of them
* `protocol KeyedDecodingContainerProtocol`: Adopted by types which provide a concrete way to retrieve encoded values from storage by `CodingKey`. Types adopting `Decoder` should provide types conforming to `KeyedDecodingContainerProtocol` to vend
* `struct KeyedDecodingContainer<Key : CodingKey>`: A concrete type-erased box for exposing `KeyedDecodingContainerProtocol` types; this is a type consumers of the API interact with directly
* `protocol UnkeyedDecodingContainer`: Adopted by types which provide a concrete way to retrieve encoded values from storage with no keys. Types adopting `Decoder` should provide types conforming to `UnkeyedDecodingContainer` 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
* `struct CodingUserInfoKey`: A `String RawRepresentable struct` for representing keys to use in `Encoders`' and `Decoders`' `userInfo` dictionaries
To support user types, we expose the `Encodable` and `Decodable` protocols:
```swift
/// Conformance to `Encodable` indicates that a type can encode itself to an external representation.
public protocol Encodable {
/// Encodes `self` into the given encoder.
///
/// If `self` fails to encode anything, `encoder` will encode an empty keyed 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
}
/// Conformance to `Decodable` indicates that a type can decode itself from an external representation.
public protocol Decodable {
/// 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
}
/// Conformance to `Codable` indicates that a type can convert itself into and out of an external representation.
public typealias Codable = Encodable & Decodable
```
By adopting these protocols, user types opt in to this system.
Structured types (i.e. types which encode as a collection of properties) encode and decode their properties in a keyed manner. Keys are semantic `String`-convertible `enums` which map properties to encoded names. Keys must conform to the `CodingKey` protocol:
```swift
/// 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 performance, where relevant, keys may be `Int`-convertible, and `Encoders` may choose to make use of `Ints` over `Strings` as appropriate. Framework types should provide keys which have both for flexibility and performance across different types of `Encoders`.
By default, `CodingKey` conformance can be derived for `enums` which have no raw type and no associated values, or `String` or `Int` backing:
```swift
enum Keys1 : CodingKey {
case a // (stringValue: "a", intValue: nil)
case b // (stringValue: "b", intValue: nil)
// The compiler automatically generates the following:
var stringValue: String {
switch self {
case .a: return "a"
case .b: return "b"
}
}
init?(stringValue: String) {
switch stringValue {
case "a": self = .a
case "b": self = .b
default: return nil
}
}
var intValue: Int? {
return nil
}
init?(intValue: Int) {
return nil
}
}
enum Keys2 : String, CodingKey {
case c = "foo" // (stringValue: "foo", intValue: nil)
case d // (stringValue: "d", intValue: nil)
// stringValue, init?(stringValue:), intValue, and init?(intValue:) are generated by the compiler as well
}
enum Keys3 : Int, CodingKey {
case e = 4 // (stringValue: "e", intValue: 4)
case f // (stringValue: "f", intValue: 5)
case g = 9 // (stringValue: "g", intValue: 9)
// stringValue, init?(stringValue:), intValue, and init?(intValue:) are generated by the compiler as well
}
```
Coding keys which are not `enum`s, have associated values, or have other raw representations must implement these methods manually.
In addition to automatic `CodingKey` requirement synthesis for `enums`, `Encodable` & `Decodable` requirements can be automatically synthesized for certain types as well:
1. Types conforming to `Encodable` whose properties are all `Encodable` get an automatically generated `String`-backed `CodingKey` `enum` mapping properties to case names. Similarly for `Decodable` types whose properties are all `Decodable`
2. Types falling into (1) — and types which manually provide a `CodingKey` `enum` (named `CodingKeys`, directly, or via a `typealias`) whose cases map 1-to-1 to `Encodable`/`Decodable` properties by name — get automatic synthesis of `init(from:)` and `encode(to:)` as appropriate, using those properties and keys
3. Types which fall into neither (1) nor (2) will have to provide a custom key type if needed and provide their own `init(from:)` and `encode(to:)`, as appropriate
This synthesis can always be overridden by a manual implementation of any protocol requirements. Many types will either allow for automatic synthesis of all of codability (1), or provide a custom key subset and take advantage of automatic method synthesis (2).
### Encoding and Decoding
Types which are `Encodable` encode their data into a container provided by their `Encoder`:
```swift
/// An `Encoder` is a type which can encode values into a native format for external representation.
public protocol Encoder {
/// Returns an encoding container appropriate for holding multiple values 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 prior `self.unkeyedContainer()` call.
/// - precondition: May not be called after a value has been encoded through a previous `self.singleValueContainer()` call.
func container<Key : CodingKey>(keyedBy type: Key.Type) -> KeyedEncodingContainer<Key>
/// Returns an encoding container appropriate for holding multiple unkeyed values.
///
/// - returns: A new empty unkeyed 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 unkeyedContainer() -> UnkeyedEncodingContainer
/// 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 prior `self.unkeyedContainer()` 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.
/// A `nil` value indicates an unkeyed container.
var codingPath: [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 latitude
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.
var 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 {
var 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:
```swift
/// 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 values with no keys.
///
/// - returns: An unkeyed container view into `self`.
/// - throws: `CocoaError.coderTypeMismatch` if the encountered stored value is not an unkeyed container.
func unkeyedContainer() throws -> UnkeyedDecodingContainer
/// 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.
/// A `nil` value indicates an unkeyed container.
var codingPath: [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 {
var 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 Containers
Keyed 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 `String` keys), and since the type is known statically, keys get autocompletion by the compiler.
```swift
/// Conformance to `KeyedEncodingContainerProtocol` indicates that a type provides a view into an `Encoder`'s storage and is used to hold the encoded properties of an `Encodable` type in a keyed manner.
///
/// Encoders should provide types conforming to `KeyedEncodingContainerProtocol` for their format.
public protocol KeyedEncodingContainerProtocol {
associatedtype 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.
mutating func encode<T : Encodable>(_ value: T?, 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.
mutating func encode(_ value: Bool?, forKey key: Key) throws
mutating func encode(_ value: Int?, forKey key: Key) throws
mutating func encode(_ value: Int8?, forKey key: Key) throws
mutating func encode(_ value: Int16?, forKey key: Key) throws
mutating func encode(_ value: Int32?, forKey key: Key) throws
mutating func encode(_ value: Int64?, forKey key: Key) throws
mutating func encode(_ value: UInt?, forKey key: Key) throws
mutating func encode(_ value: UInt8?, forKey key: Key) throws
mutating func encode(_ value: UInt16?, forKey key: Key) throws
mutating func encode(_ value: UInt32?, forKey key: Key) throws
mutating func encode(_ value: UInt64?, forKey key: Key) throws
mutating func encode(_ value: Float?, forKey key: Key) throws
mutating func encode(_ value: Double?, forKey key: Key) throws
mutating func encode(_ value: String?, 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 is encoded unconditionally elsewhere in the payload (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.
mutating func encodeWeak<T : AnyObject & Encodable>(_ object: T?, forKey key: Key) throws
/// The path of coding keys taken to get to this point in encoding.
/// A `nil` value indicates an unkeyed container.
var codingPath: [CodingKey?] { get }
}
/// `KeyedEncodingContainer` is a type-erased box for `KeyedEncodingContainerProtocol` types, similar to `AnyCollection` and `AnyHashable`. This is the type which consumers of the API interact with directly.
public struct KeyedEncodingContainer<K : CodingKey> : KeyedEncodingContainerProtocol {
associatedtype Key = K
/// Initializes `self` with the given container.
///
/// - parameter container: The container to hold.
init<Container : KeyedEncodingContainerProtocol>(_ container: Container) where Container.Key == Key
// + methods from KeyedEncodingContainerProtocol
}
/// Conformance to `KeyedDecodingContainerProtocol` indicates that a type provides a view into a `Decoder`'s storage and is used to hold the encoded properties of a `Decodable` type in a keyed manner.
///
/// Decoders should provide types conforming to `KeyedDecodingContainerProtocol` for their format.
public protocol KeyedDecodingContainerProtocol {
associatedtype 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.
var allKeys: [Key] { get }
/// 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.
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 `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.
func decode(_ type: Bool.Type, forKey key: Key) throws -> Bool
func decode(_ type: Int.Type, forKey key: Key) throws -> Int
func decode(_ type: Int8.Type, forKey key: Key) throws -> Int8
func decode(_ type: Int16.Type, forKey key: Key) throws -> Int16
func decode(_ type: Int32.Type, forKey key: Key) throws -> Int32
func decode(_ type: Int64.Type, forKey key: Key) throws -> Int64
func decode(_ type: UInt.Type, forKey key: Key) throws -> UInt
func decode(_ type: UInt8.Type, forKey key: Key) throws -> UInt8
func decode(_ type: UInt16.Type, forKey key: Key) throws -> UInt16
func decode(_ type: UInt32.Type, forKey key: Key) throws -> UInt32
func decode(_ type: UInt64.Type, forKey key: Key) throws -> UInt64
func decode(_ type: Float.Type, forKey key: Key) throws -> Float
func decode(_ type: Double.Type, forKey key: Key) throws -> Double
func decode(_ type: String.Type, forKey key: Key) throws -> String
func decode<T : Decodable>(_ type: T.Type, forKey key: Key) throws -> T
/// 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 distinguished 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.
func decodeIfPresent(_ type: Bool.Type, forKey key: Key) throws -> Bool?
func decodeIfPresent(_ type: Int.Type, forKey key: Key) throws -> Int?
func decodeIfPresent(_ type: Int8.Type, forKey key: Key) throws -> Int8?
func decodeIfPresent(_ type: Int16.Type, forKey key: Key) throws -> Int16?
func decodeIfPresent(_ type: Int32.Type, forKey key: Key) throws -> Int32?
func decodeIfPresent(_ type: Int64.Type, forKey key: Key) throws -> Int64?
func decodeIfPresent(_ type: UInt.Type, forKey key: Key) throws -> UInt?
func decodeIfPresent(_ type: UInt8.Type, forKey key: Key) throws -> UInt8?
func decodeIfPresent(_ type: UInt16.Type, forKey key: Key) throws -> UInt16?
func decodeIfPresent(_ type: UInt32.Type, forKey key: Key) throws -> UInt32?
func decodeIfPresent(_ type: UInt64.Type, forKey key: Key) throws -> UInt64?
func decodeIfPresent(_ type: Float.Type, forKey key: Key) throws -> Float?
func decodeIfPresent(_ type: Double.Type, forKey key: Key) throws -> Double?
func decodeIfPresent(_ type: String.Type, forKey key: Key) throws -> String?
func decodeIfPresent<T : Decodable>(_ type: T.Type, forKey key: Key) throws -> T?
/// The path of coding keys taken to get to this point in decoding.
/// A `nil` value indicates an unkeyed container.
var codingPath: [CodingKey?] { get }
}
/// `KeyedDecodingContainer` is a type-erased box for `KeyedDecodingContainerProtocol` types, similar to `AnyCollection` and `AnyHashable`. This is the type which consumers of the API interact with directly.
public struct KeyedDecodingContainer<K : CodingKey> : KeyedDecodingContainerProtocol {
associatedtype Key = K
/// Initializes `self` with the given container.
///
/// - parameter container: The container to hold.
init<Container : KeyedDecodingContainerProtocol>(_ container: Container) where Container.Key == Key
// + methods from KeyedDecodingContainerProtocol
}
```
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.
When the conditional conformance feature lands in Swift, the ability to express that "a collection of things which are `Codable` is `Codable`" will allow collections (`Array`, `Dictionary`, etc.) to be extended and fall into these overloads as well.
### Unkeyed Containers
For some types, when the source and destination of a payload can be guaranteed to agree on the payload layout and format (e.g. in cross-process communication, where both sides agree on the payload format), it may be appropriate to eschew the encoding of keys and encode sequentially, without keys. In this case, a type may choose to make use of an unkeyed container for its properties:
```swift
/// Conformance to `UnkeyedEncodingContainer` indicates that a type provides a view into an `Encoder`'s storage and is used to hold the encoded properties of an `Encodable` type sequentially, without keys.
///
/// Encoders should provide types conforming to `UnkeyedEncodingContainer` for their format.
public protocol UnkeyedEncodingContainer {
/// Encodes the given value.
///
/// - parameter value: The value to encode.
/// - throws: `CocoaError.coderInvalidValue` if the given value is invalid in the current context for this format.
mutating func encode<T : Encodable>(_ value: T?) throws
/// Encodes the given value.
///
/// - parameter value: The value to encode.
/// - throws: `CocoaError.coderInvalidValue` if the given value is invalid in the current context for this format.
mutating func encode(_ value: Bool?) throws
mutating func encode(_ value: Int?) throws
mutating func encode(_ value: Int8?) throws
mutating func encode(_ value: Int16?) throws
mutating func encode(_ value: Int32?) throws
mutating func encode(_ value: Int64?) throws
mutating func encode(_ value: UInt?) throws
mutating func encode(_ value: UInt8?) throws
mutating func encode(_ value: UInt16?) throws
mutating func encode(_ value: UInt32?) throws
mutating func encode(_ value: UInt64?) throws
mutating func encode(_ value: Float?) throws
mutating func encode(_ value: Double?) throws
mutating func encode(_ value: String?) throws
/// Encodes the given object weakly.
///
/// For `Encoder`s that implement this functionality, this will only encode the given object if it is encoded unconditionally elsewhere in the payload (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.
/// - throws: `CocoaError.coderInvalidValue` if the given value is invalid in the current context for this format.
mutating func encodeWeak<T : AnyObject & Encodable>(_ object: T?) throws
/// Encodes the elements of the given sequence.
///
/// A default implementation of these is given in an extension.
///
/// - parameter sequence: The sequences whose contents to encode.
/// - throws: An error if any of the contained values throws an error.
mutating func encode<Sequence : Swift.Sequence>(contentsOf sequence: Sequence) throws where Sequence.Iterator.Element == Bool
mutating func encode<Sequence : Swift.Sequence>(contentsOf sequence: Sequence) throws where Sequence.Iterator.Element == Int
// ...
mutating func encode<Sequence : Swift.Sequence>(contentsOf sequence: Sequence) throws where Sequence.Iterator.Element : Encodable
/// The path of coding keys taken to get to this point in encoding.
/// A `nil` value indicates an unkeyed container.
var codingPath: [CodingKey?] { get }
}
/// Conformance to `UnkeyedDecodingContainer` indicates that a type provides a view into a `Decoder`'s storage and is used to hold the encoded properties of a `Decodable` type sequentially, without keys.
///
/// Decoders should provide types conforming to `UnkeyedDecodingContainer` for their format.
public protocol UnkeyedDecodingContainer {
/// Returns the number of elements (if known) contained within this container.
var count: Int? { get }
/// Returns whether there are no more elements left to be decoded in the container.
var isAtEnd: Bool { get }
/// Decodes a value of the given type.
///
/// A default implementation is given for these types which calls into the `decodeIfPresent` implementations below.
///
/// - parameter type: The type of value to decode.
/// - 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 the encountered encoded value is null, or of there are no more values to decode.
mutating func decode(_ type: Bool.Type) throws -> Bool
mutating func decode(_ type: Int.Type) throws -> Int
mutating func decode(_ type: Int8.Type) throws -> Int8
mutating func decode(_ type: Int16.Type) throws -> Int16
mutating func decode(_ type: Int32.Type) throws -> Int32
mutating func decode(_ type: Int64.Type) throws -> Int64
mutating func decode(_ type: UInt.Type) throws -> UInt
mutating func decode(_ type: UInt8.Type) throws -> UInt8
mutating func decode(_ type: UInt16.Type) throws -> UInt16
mutating func decode(_ type: UInt32.Type) throws -> UInt32
mutating func decode(_ type: UInt64.Type) throws -> UInt64
mutating func decode(_ type: Float.Type) throws -> Float
mutating func decode(_ type: Double.Type) throws -> Double
mutating func decode(_ type: String.Type) throws -> String
mutating func decode<T : Decodable>(_ type: T.Type) throws -> T
/// Decodes a value of the given type, if present.
///
/// This method returns `nil` if the container has no elements left to decode, or if the value is null. The difference between these states can be distinguished by checking `isAtEnd`.
///
/// - parameter type: The type of value to decode.
/// - returns: A decoded value of the requested type, or `nil` if the value is a null value, or if there are no more elements to decode.
/// - throws: `CocoaError.coderTypeMismatch` if the encountered encoded value is not convertible to the requested type.
mutating func decodeIfPresent(_ type: Bool.Type) throws -> Bool?
mutating func decodeIfPresent(_ type: Int.Type) throws -> Int?
mutating func decodeIfPresent(_ type: Int8.Type) throws -> Int8?
mutating func decodeIfPresent(_ type: Int16.Type) throws -> Int16?
mutating func decodeIfPresent(_ type: Int32.Type) throws -> Int32?
mutating func decodeIfPresent(_ type: Int64.Type) throws -> Int64?
mutating func decodeIfPresent(_ type: UInt.Type) throws -> UInt?
mutating func decodeIfPresent(_ type: UInt8.Type) throws -> UInt8?
mutating func decodeIfPresent(_ type: UInt16.Type) throws -> UInt16?
mutating func decodeIfPresent(_ type: UInt32.Type) throws -> UInt32?
mutating func decodeIfPresent(_ type: UInt64.Type) throws -> UInt64?
mutating func decodeIfPresent(_ type: Float.Type) throws -> Float?
mutating func decodeIfPresent(_ type: Double.Type) throws -> Double?
mutating func decodeIfPresent(_ type: String.Type) throws -> String?
mutating func decodeIfPresent<T : Decodable>(_ type: T.Type) throws -> T?
/// The path of coding keys taken to get to this point in decoding.
/// A `nil` value indicates an unkeyed container.
var codingPath: [CodingKey?] { get }
}
```
Unkeyed encoding is fragile and generally not appropriate for archival without specific format guarantees, so keyed encoding remains the recommended approach (and is why `CodingKey` `enums` are synthesized by default unless otherwise declined).
### 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 an outer container:
```swift
/// 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.
mutating func encode(_ value: Bool) throws
mutating func encode(_ value: Int) throws
mutating func encode(_ value: Int8) throws
mutating func encode(_ value: Int16) throws
mutating func encode(_ value: Int32) throws
mutating func encode(_ value: Int64) throws
mutating func encode(_ value: UInt) throws
mutating func encode(_ value: UInt8) throws
mutating func encode(_ value: UInt16) throws
mutating func encode(_ value: UInt32) throws
mutating func encode(_ value: UInt64) throws
mutating func encode(_ value: Float) throws
mutating func encode(_ value: Double) throws
mutating func encode(_ value: String) 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
}
// 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 types:
```swift
// Continuing from before
public protocol KeyedEncodingContainerProtocol {
/// Stores a keyed encoding container for the given key and returns it.
///
/// - parameter keyType: The key type to use for the container.
/// - parameter key: The key to encode the container for.
/// - returns: A new keyed encoding container.
mutating func nestedContainer<NestedKey : CodingKey>(keyedBy keyType: NestedKey.Type, forKey key: Key) -> KeyedEncodingContainer<NestedKey>
/// Stores an unkeyed encoding container for the given key and returns it.
///
/// - parameter key: The key to encode the container for.
/// - returns: A new unkeyed encoding container.
mutating func nestedUnkeyedContainer(forKey key: Key) -> UnkeyedEncodingContainer
}
public protocol KeyedDecodingContainerProtocol {
/// 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 keyed container.
func nestedContainer<NestedKey : CodingKey>(keyedBy type: NestedKey.Type, forKey key: Key) throws -> KeyedDecodingContainer<NestedKey>
/// Returns the data stored for the given key as represented in an unkeyed container.
///
/// - parameter key: The key that the nested container is associated with.
/// - returns: An unkeyed decoding container view into `self`.
/// - throws: `CocoaError.coderTypeMismatch` if the encountered stored value is not an unkeyed container.
func nestedUnkeyedContainer(forKey key: Key) throws -> UnkeyedDecodingContainer
}
```
This can be common when coding against specific external data representations:
```swift
// 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 {
var 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, 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)
}
}
```
Unkeyed containers allow for the same types of nesting:
```swift
// Continuing from before
public protocol UnkeyedEncodingContainer {
/// Encodes a nested container keyed by the given type and returns it.
///
/// - parameter keyType: The key type to use for the container.
/// - returns: A new keyed encoding container.
mutating func nestedContainer<NestedKey : CodingKey>(keyedBy keyType: NestedKey.Type) -> KeyedEncodingContainer<NestedKey>
/// Encodes an unkeyed encoding container and returns it.
///
/// - returns: A new unkeyed encoding container.
mutating func nestedUnkeyedContainer() -> UnkeyedEncodingContainer
}
public protocol UnkeyedDecodingContainer {
/// Decodes a nested container keyed by the given 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.
mutating func nestedContainer<NestedKey : CodingKey>(keyedBy type: NestedKey.Type) throws -> KeyedDecodingContainer<NestedKey>
/// Decodes an unkeyed nested container.
///
/// - returns: An unkeyed decoding container view into `self`.
/// - throws: `CocoaError.coderTypeMismatch` if the encountered stored value is not an unkeyed container.
mutating func nestedUnkeyedContainer() throws -> UnkeyedDecodingContainer
}
```
### Dynamic Context-Based Behavior
In some cases, types may need context in order to decide on their external representation. Some types may choose a different representation based on the encoding format that they are being read from or written to, and others based on other runtime contextual information. To facilitate this, `Encoders` and `Decoders` expose user-supplied context for consumption:
```swift
/// Represents a user-defined key for providing context for encoding and decoding.
public struct CodingUserInfoKey : RawRepresentable, Hashable {
typealias RawValue = String
let rawValue: String
init?(rawValue: String)
}
// Continuing from before:
public protocol Encoder {
/// Any contextual information set by the user for encoding.
var userInfo: [CodingUserInfoKey : Any] { get }
}
public protocol Decoder {
/// Any contextual information set by the user for decoding.
var userInfo: [CodingUserInfoKey : Any] { get }
}
```
Consuming types may then support setting contextual information to inform their encoding and decoding:
```swift
public struct Person : Encodable {
public static let codingUserInfoKey = CodingUserInfoKey("com.foocorp.person.codingUserInfoKey")
public struct UserInfo {
let shouldEncodePrivateFields: Bool
// ...
}
func encode(to encoder: Encoder) throws {
if let context = encoder.userInfo[Person.codingUserInfoKey] as? Person.UserInfo {
if context.shouldEncodePrivateFields {
// Do something special.
}
}
// Fall back to default.
}
}
let encoder = ...
encoder.userInfo[Person.codingUserInfoKey] = Person.UserInfo(...)
let data = try encoder.encode(person)
```
`Encoders` and `Decoders` may choose to expose contextual information about their configuration as part of the context as well if necessary.
### Inheritance
Inheritance in this system is supported much like it is with `NSCoding` — on encoding, objects which inherit from a type that is `Encodable` encode `super` using their encoder, and pass a decoder to `super.init(from:)` on decode if they inherit from a type that is `Decodable`. With the existing `NSCoding` API, this is most often done like so, by convention:
```objc
- (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 encoded as `{"a": ..., "b": ..., "c": ..., "d": ..., "e": ..., "f": ...}`. This approach has two drawbacks:
1. Things which `self` encodes may overwrite `super`'s (or vice versa, depending on when `-[super encodeWithCoder:]` is called
2. `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 (`.array` and `.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`:
```swift
public class MyCodable : SomethingCodable {
public func encode(to encoder: Encoder) throws {
var 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 containers to provide handles to nested containers for `super` to use.
```swift
// Continuing from before
public protocol KeyedEncodingContainerProtocol {
/// 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:)`.
mutating 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:)`.
mutating func superEncoder(forKey key: Key) -> Encoder
}
public protocol KeyedDecodingContainerProtocol {
/// 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.
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.
func superDecoder(forKey key: Key) throws -> Decoder
}
public protocol UnkeyedEncodingContainer {
/// Encodes a nested container and returns an `Encoder` instance for encoding `super` into that container.
///
/// - returns: A new `Encoder` to pass to `super.encode(to:)`.
mutating func superEncoder() -> Encoder
}
public protocol UnkeyedDecodingContainer {
/// Decodes a nested container and returns a `Decoder` instance for decoding `super` from that container.
///
/// - returns: A new `Decoder` to pass to `super.init(from:)`.
/// - throws: `CocoaError.coderValueNotFound` if the encountered encoded value is null, or of there are no more values to decode.
mutating func superDecoder() 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 `Codable` conformance on these types:
```swift
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.
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)`).
#### Additional Extensions
Along with the primitive `Codable` conformance above, extensions on `Codable` `RawRepresentable` types whose `RawValue` is a primitive types will provide default implementations for encoding and decoding:
```swift
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...
```