Skip to content

Commit d54fcb9

Browse files
authored
Merge pull request #1048 from bubski/json-encoder
2 parents ee845e3 + b23ca90 commit d54fcb9

File tree

11 files changed

+3293
-22
lines changed

11 files changed

+3293
-22
lines changed

Foundation.xcodeproj/project.pbxproj

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,9 @@
1313
231503DB1D8AEE5D0061694D /* TestNSDecimal.swift in Sources */ = {isa = PBXBuildFile; fileRef = 231503DA1D8AEE5D0061694D /* TestNSDecimal.swift */; };
1414
294E3C1D1CC5E19300E4F44C /* TestNSAttributedString.swift in Sources */ = {isa = PBXBuildFile; fileRef = 294E3C1C1CC5E19300E4F44C /* TestNSAttributedString.swift */; };
1515
2EBE67A51C77BF0E006583D5 /* TestDateFormatter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2EBE67A31C77BF05006583D5 /* TestDateFormatter.swift */; };
16+
3EA9D6701EF0532D00B362D6 /* TestJSONEncoder.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3EA9D66F1EF0532D00B362D6 /* TestJSONEncoder.swift */; };
17+
3EDCE50C1EF04D8100C2EC04 /* Codable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3EDCE5051EF04D8100C2EC04 /* Codable.swift */; };
18+
3EDCE5101EF04D8100C2EC04 /* JSONEncoder.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3EDCE5091EF04D8100C2EC04 /* JSONEncoder.swift */; };
1619
528776141BF2629700CB0090 /* FoundationErrors.swift in Sources */ = {isa = PBXBuildFile; fileRef = 522C253A1BF16E1600804FC6 /* FoundationErrors.swift */; };
1720
528776191BF27D9500CB0090 /* Test.plist in Resources */ = {isa = PBXBuildFile; fileRef = 528776181BF27D9500CB0090 /* Test.plist */; };
1821
555683BD1C1250E70041D4C6 /* TestNSUserDefaults.swift in Sources */ = {isa = PBXBuildFile; fileRef = 555683BC1C1250E70041D4C6 /* TestNSUserDefaults.swift */; };
@@ -482,6 +485,9 @@
482485
231503DA1D8AEE5D0061694D /* TestNSDecimal.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TestNSDecimal.swift; sourceTree = "<group>"; };
483486
294E3C1C1CC5E19300E4F44C /* TestNSAttributedString.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TestNSAttributedString.swift; sourceTree = "<group>"; };
484487
2EBE67A31C77BF05006583D5 /* TestDateFormatter.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TestDateFormatter.swift; sourceTree = "<group>"; };
488+
3EA9D66F1EF0532D00B362D6 /* TestJSONEncoder.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TestJSONEncoder.swift; sourceTree = "<group>"; };
489+
3EDCE5051EF04D8100C2EC04 /* Codable.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Codable.swift; sourceTree = "<group>"; };
490+
3EDCE5091EF04D8100C2EC04 /* JSONEncoder.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = JSONEncoder.swift; sourceTree = "<group>"; };
485491
400E22641C1A4E58007C5933 /* TestProcessInfo.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TestProcessInfo.swift; sourceTree = "<group>"; };
486492
4AE109261C17CCBF007367B5 /* TestNSIndexPath.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TestNSIndexPath.swift; sourceTree = "<group>"; };
487493
4DC1D07F1C12EEEF00B5948A /* TestNSPipe.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TestNSPipe.swift; sourceTree = "<group>"; };
@@ -959,6 +965,15 @@
959965
/* End PBXFrameworksBuildPhase section */
960966

961967
/* Begin PBXGroup section */
968+
3EDCE5121EF04D8600C2EC04 /* JSONEncoder */ = {
969+
isa = PBXGroup;
970+
children = (
971+
3EDCE5051EF04D8100C2EC04 /* Codable.swift */,
972+
3EDCE5091EF04D8100C2EC04 /* JSONEncoder.swift */,
973+
);
974+
name = JSONEncoder;
975+
sourceTree = "<group>";
976+
};
962977
5B1FD9C71D6D162D0080E83C /* Session */ = {
963978
isa = PBXGroup;
964979
children = (
@@ -1405,6 +1420,9 @@
14051420
BD8042151E09857800487EB8 /* TestLengthFormatter.swift */,
14061421
A058C2011E529CF100B07AA1 /* TestMassFormatter.swift */,
14071422
BF8E65301DC3B3CB005AB5C3 /* TestNotification.swift */,
1423+
3EA9D66F1EF0532D00B362D6 /* TestJSONEncoder.swift */,
1424+
159884911DCC877700E3314C /* TestNSHTTPCookieStorage.swift */,
1425+
D4FE895A1D703D1100DA7986 /* TestURLRequest.swift */,
14081426
C93559281C12C49F009FD6A9 /* TestNSAffineTransform.swift */,
14091427
EA66F63C1BF1619600136161 /* TestNSArray.swift */,
14101428
294E3C1C1CC5E19300E4F44C /* TestNSAttributedString.swift */,
@@ -1593,6 +1611,7 @@
15931611
EAB57B6C1BD1A852004AC5C5 /* Serialization */ = {
15941612
isa = PBXGroup;
15951613
children = (
1614+
3EDCE5121EF04D8600C2EC04 /* JSONEncoder */,
15961615
EADE0B641BD15DFF00C49C64 /* NSJSONSerialization.swift */,
15971616
EADE0B651BD15DFF00C49C64 /* NSKeyedArchiver.swift */,
15981617
D3BCEB9F1C2F6DDB00295652 /* NSKeyedCoderOldStyleArray.swift */,
@@ -2141,6 +2160,7 @@
21412160
D3E8D6D11C367AB600295652 /* NSSpecialValue.swift in Sources */,
21422161
EAB57B721BD1C7A5004AC5C5 /* NSPortMessage.swift in Sources */,
21432162
5BD31D201D5CE8C400563814 /* Bridging.swift in Sources */,
2163+
3EDCE50C1EF04D8100C2EC04 /* Codable.swift in Sources */,
21442164
EADE0BBB1BD15E0000C49C64 /* NSURLAuthenticationChallenge.swift in Sources */,
21452165
EADE0BA11BD15DFF00C49C64 /* NSIndexSet.swift in Sources */,
21462166
5BF7AEA91BCD51F9008F214A /* NSDate.swift in Sources */,
@@ -2156,6 +2176,7 @@
21562176
5BD31D411D5D1BC300563814 /* Set.swift in Sources */,
21572177
5BD31D241D5CECC400563814 /* Array.swift in Sources */,
21582178
5BF7AEBC1BCD51F9008F214A /* Thread.swift in Sources */,
2179+
3EDCE5101EF04D8100C2EC04 /* JSONEncoder.swift in Sources */,
21592180
D31302011C30CEA900295652 /* NSConcreteValue.swift in Sources */,
21602181
5BF7AEA81BCD51F9008F214A /* NSData.swift in Sources */,
21612182
5B424C761D0B6E5B007B39C8 /* IndexPath.swift in Sources */,
@@ -2396,6 +2417,7 @@
23962417
5B13B3371C582D4C00651CE2 /* TestNSNotificationCenter.swift in Sources */,
23972418
5B13B3251C582D4700651CE2 /* main.swift in Sources */,
23982419
5B1FD9E31D6D17B80080E83C /* TestNSURLSession.swift in Sources */,
2420+
3EA9D6701EF0532D00B362D6 /* TestJSONEncoder.swift in Sources */,
23992421
D512D17C1CD883F00032E6A5 /* TestFileHandle.swift in Sources */,
24002422
D4FE895B1D703D1100DA7986 /* TestURLRequest.swift in Sources */,
24012423
5B13B33A1C582D4C00651CE2 /* TestNSNumber.swift in Sources */,

Foundation/Codable.swift

Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
// This source file is part of the Swift.org open source project
2+
//
3+
// Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors
4+
// Licensed under Apache License v2.0 with Runtime Library Exception
5+
//
6+
// See http://swift.org/LICENSE.txt for license information
7+
// See http://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
8+
//
9+
10+
//===----------------------------------------------------------------------===//
11+
// Errors
12+
//===----------------------------------------------------------------------===//
13+
14+
// Adding the following extensions to EncodingError and DecodingError allows them to bridge to NSErrors implicitly.
15+
16+
fileprivate let NSCodingPathErrorKey = "NSCodingPath"
17+
fileprivate let NSDebugDescriptionErrorKey = "NSDebugDescription"
18+
19+
extension EncodingError : CustomNSError {
20+
public static var errorDomain: String = NSCocoaErrorDomain
21+
22+
public var errorCode: Int {
23+
switch self {
24+
case .invalidValue(_, _): return CocoaError.coderInvalidValue.rawValue
25+
}
26+
}
27+
28+
public var errorUserInfo: [String : Any] {
29+
let context: Context
30+
switch self {
31+
case .invalidValue(_, let c): context = c
32+
}
33+
34+
return [NSCodingPathErrorKey: context.codingPath,
35+
NSDebugDescriptionErrorKey: context.debugDescription]
36+
}
37+
}
38+
39+
extension DecodingError : CustomNSError {
40+
public static var errorDomain: String = NSCocoaErrorDomain
41+
42+
public var errorCode: Int {
43+
switch self {
44+
case .valueNotFound(_, _): fallthrough
45+
case .keyNotFound(_, _):
46+
return CocoaError.coderValueNotFound.rawValue
47+
48+
case .typeMismatch(_, _): fallthrough
49+
case .dataCorrupted(_):
50+
return CocoaError.coderReadCorrupt.rawValue
51+
}
52+
}
53+
54+
public var errorUserInfo: [String : Any] {
55+
let context: Context
56+
switch self {
57+
case .typeMismatch(_, let c): context = c
58+
case .valueNotFound(_, let c): context = c
59+
case .keyNotFound(_, let c): context = c
60+
case .dataCorrupted(let c): context = c
61+
}
62+
63+
return [NSCodingPathErrorKey: context.codingPath,
64+
NSDebugDescriptionErrorKey: context.debugDescription]
65+
}
66+
}
67+
68+
//===----------------------------------------------------------------------===//
69+
// Error Utilities
70+
//===----------------------------------------------------------------------===//
71+
72+
internal extension DecodingError {
73+
/// Returns a `.typeMismatch` error describing the expected type.
74+
///
75+
/// - parameter path: The path of `CodingKey`s taken to decode a value of this type.
76+
/// - parameter expectation: The type expected to be encountered.
77+
/// - parameter reality: The value that was encountered instead of the expected type.
78+
/// - returns: A `DecodingError` with the appropriate path and debug description.
79+
internal static func _typeMismatch(at path: [CodingKey?], expectation: Any.Type, reality: Any) -> DecodingError {
80+
let description = "Expected to decode \(expectation) but found \(_typeDescription(of: reality)) instead."
81+
return .typeMismatch(expectation, Context(codingPath: path, debugDescription: description))
82+
}
83+
84+
/// Returns a description of the type of `value` appropriate for an error message.
85+
///
86+
/// - parameter value: The value whose type to describe.
87+
/// - returns: A string describing `value`.
88+
/// - precondition: `value` is one of the types below.
89+
fileprivate static func _typeDescription(of value: Any) -> String {
90+
if value is NSNull {
91+
return "a null value"
92+
} else if value is NSNumber /* FIXME: If swift-corelibs-foundation isn't updated to use NSNumber, this check will be necessary: || value is Int || value is Double */ {
93+
return "a number"
94+
} else if value is String {
95+
return "a string/data"
96+
} else if value is [Any] {
97+
return "an array"
98+
} else if value is [String : Any] {
99+
return "a dictionary"
100+
} else {
101+
// This should never happen -- we somehow have a non-JSON type here.
102+
preconditionFailure("Invalid storage type \(type(of: value)).")
103+
}
104+
}
105+
}

Foundation/Date.swift

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -266,3 +266,16 @@ extension Date : CustomPlaygroundQuickLookable {
266266
return .text(summary)
267267
}
268268
}
269+
270+
extension Date : Codable {
271+
public init(from decoder: Decoder) throws {
272+
let container = try decoder.singleValueContainer()
273+
let timestamp = try container.decode(Double.self)
274+
self.init(timeIntervalSinceReferenceDate: timestamp)
275+
}
276+
277+
public func encode(to encoder: Encoder) throws {
278+
var container = encoder.singleValueContainer()
279+
try container.encode(self.timeIntervalSinceReferenceDate)
280+
}
281+
}

0 commit comments

Comments
 (0)