Skip to content

Commit 3919af1

Browse files
authored
Merge pull request #2167 from millenomi/iso8601dateformatter-nscoding
2 parents bf79c1e + fee92bd commit 3919af1

File tree

6 files changed

+131
-13
lines changed

6 files changed

+131
-13
lines changed

Foundation/ISO8601DateFormatter.swift

Lines changed: 38 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -79,8 +79,44 @@ open class ISO8601DateFormatter : Formatter, NSSecureCoding {
7979
super.init()
8080
}
8181

82-
public required init?(coder aDecoder: NSCoder) { NSUnimplemented() }
83-
open override func encode(with aCoder: NSCoder) { NSUnimplemented() }
82+
public required init?(coder aDecoder: NSCoder) {
83+
guard aDecoder.allowsKeyedCoding else {
84+
fatalError("Decoding ISO8601DateFormatter requires a coder that allows keyed coding")
85+
}
86+
87+
self.formatOptions = Options(rawValue: UInt(aDecoder.decodeInteger(forKey: "NS.formatOptions")))
88+
89+
let timeZone: NSTimeZone?
90+
91+
if aDecoder.containsValue(forKey: "NS.timeZone") {
92+
if let tz = aDecoder.decodeObject(of: NSTimeZone.self, forKey: "NS.timeZone") {
93+
timeZone = tz
94+
} else {
95+
aDecoder.failWithError(CocoaError(.coderReadCorrupt, userInfo: [ NSLocalizedDescriptionKey: "Time zone was corrupt while decoding ISO8601DateFormatter" ]))
96+
return nil
97+
}
98+
} else {
99+
timeZone = nil
100+
}
101+
102+
if let zone = timeZone?._swiftObject {
103+
self.timeZone = zone
104+
}
105+
106+
super.init()
107+
}
108+
109+
open override func encode(with aCoder: NSCoder) {
110+
guard aCoder.allowsKeyedCoding else {
111+
fatalError("Encoding ISO8601DateFormatter requires a coder that allows keyed coding")
112+
}
113+
114+
aCoder.encode(Int(formatOptions.rawValue), forKey: "NS.formatOptions")
115+
if let timeZone = timeZone {
116+
aCoder.encode(timeZone._nsObject, forKey: "NS.timeZone")
117+
}
118+
}
119+
84120
public static var supportsSecureCoding: Bool { return true }
85121

86122
open func string(from date: Date) -> String {

TestFoundation/FixtureValues.swift

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -94,7 +94,6 @@ enum Fixtures {
9494
}
9595

9696
static let dateIntervalFormatterValuesSetWithoutTemplate = TypedFixture<DateIntervalFormatter>("DateIntervalFormatter-ValuesSetWithoutTemplate") {
97-
9897
let dif = DateIntervalFormatter()
9998

10099
var calendar = Calendar.neutral
@@ -111,7 +110,6 @@ enum Fixtures {
111110
}
112111

113112
static let dateIntervalFormatterValuesSetWithTemplate = TypedFixture<DateIntervalFormatter>("DateIntervalFormatter-ValuesSetWithTemplate") {
114-
115113
let dif = DateIntervalFormatter()
116114

117115
var calendar = Calendar.neutral
@@ -126,6 +124,24 @@ enum Fixtures {
126124
return dif
127125
}
128126

127+
// ===== ISO8601DateFormatter =====
128+
129+
static let iso8601FormatterDefault = TypedFixture<ISO8601DateFormatter>("ISO8601DateFormatter-Default") {
130+
let idf = ISO8601DateFormatter()
131+
idf.timeZone = Calendar.neutral.timeZone
132+
133+
return idf
134+
}
135+
136+
static let iso8601FormatterOptionsSet = TypedFixture<ISO8601DateFormatter>("ISO8601DateFormatter-OptionsSet") {
137+
let idf = ISO8601DateFormatter()
138+
idf.timeZone = Calendar.neutral.timeZone
139+
140+
idf.formatOptions = [ .withDay, .withWeekOfYear, .withMonth, .withTimeZone, .withColonSeparatorInTimeZone, .withDashSeparatorInDate ]
141+
142+
return idf
143+
}
144+
129145
// ===== Fixture list =====
130146

131147
static let _listOfAllFixtures: [AnyFixture] = [
@@ -136,6 +152,8 @@ enum Fixtures {
136152
AnyFixture(Fixtures.dateIntervalFormatterDefault),
137153
AnyFixture(Fixtures.dateIntervalFormatterValuesSetWithTemplate),
138154
AnyFixture(Fixtures.dateIntervalFormatterValuesSetWithoutTemplate),
155+
AnyFixture(Fixtures.iso8601FormatterDefault),
156+
AnyFixture(Fixtures.iso8601FormatterOptionsSet),
139157
]
140158

141159
// This ensures that we do not have fixtures with duplicate identifiers:
Binary file not shown.
Binary file not shown.

TestFoundation/TestISO8601DateFormatter.swift

Lines changed: 32 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -9,15 +9,6 @@
99

1010
class TestISO8601DateFormatter: XCTestCase {
1111

12-
static var allTests : [(String, (TestISO8601DateFormatter) -> () throws -> Void)] {
13-
14-
return [
15-
("test_stringFromDate", test_stringFromDate),
16-
("test_dateFromString", test_dateFromString),
17-
("test_stringFromDateClass", test_stringFromDateClass),
18-
]
19-
}
20-
2112
func test_stringFromDate() {
2213
let formatter = DateFormatter()
2314
formatter.dateFormat = "yyyy/MM/dd HH:mm:ss.SSSS zzz"
@@ -318,4 +309,36 @@ class TestISO8601DateFormatter: XCTestCase {
318309
#endif
319310
}
320311

312+
let fixtures = [
313+
Fixtures.iso8601FormatterDefault,
314+
Fixtures.iso8601FormatterOptionsSet
315+
]
316+
317+
func areEqual(_ a: ISO8601DateFormatter, _ b: ISO8601DateFormatter) -> Bool {
318+
return a.formatOptions == b.formatOptions &&
319+
a.timeZone.identifier == b.timeZone.identifier
320+
}
321+
322+
func test_codingRoundtrip() throws {
323+
for fixture in fixtures {
324+
try fixture.assertValueRoundtripsInCoder(secureCoding: true, matchingWith: areEqual(_:_:))
325+
}
326+
}
327+
328+
func test_loadingFixtures() throws {
329+
for fixture in fixtures {
330+
try fixture.assertLoadedValuesMatch(areEqual(_:_:))
331+
}
332+
}
333+
334+
static var allTests : [(String, (TestISO8601DateFormatter) -> () throws -> Void)] {
335+
336+
return [
337+
("test_stringFromDate", test_stringFromDate),
338+
("test_dateFromString", test_dateFromString),
339+
("test_stringFromDateClass", test_stringFromDateClass),
340+
("test_codingRoundtrip", test_codingRoundtrip),
341+
("test_loadingFixtures", test_loadingFixtures),
342+
]
343+
}
321344
}

TestFoundation/Utilities.swift

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -191,6 +191,47 @@ extension Fixture where ValueType: NSObject & NSCoding {
191191
func loadEach(handler: (ValueType, FixtureVariant) throws -> Void) throws {
192192
try self.loadEach(fixtureRepository: try testBundle().url(forResource: "Fixtures", withExtension: nil).unwrapped(), handler: handler)
193193
}
194+
195+
func assertLoadedValuesMatch(_ matchHandler: (ValueType, ValueType) -> Bool = { $0 == $1 }) throws {
196+
let reference = try make()
197+
try loadEach(handler: { (value, variant) in
198+
XCTAssertTrue(matchHandler(reference, value), "The fixture with identifier \(identifier) failed to match for on-disk variant \(variant)")
199+
})
200+
}
201+
202+
func assertValueRoundtripsInCoder(settingUpArchiverWith archiverSetup: (NSKeyedArchiver) -> Void = { _ in}, unarchiverWith unarchiverSetup: (NSKeyedUnarchiver) -> Void = { _ in}, matchingWith: (ValueType, ValueType) -> Bool = { $0 == $1 }) throws {
203+
let original = try make()
204+
205+
let coder = NSKeyedArchiver(forWritingWith: NSMutableData())
206+
coder.decodingFailurePolicy = .setErrorAndReturn
207+
archiverSetup(coder)
208+
209+
coder.encode(original, forKey: NSKeyedArchiveRootObjectKey)
210+
coder.finishEncoding()
211+
212+
let data = coder.encodedData
213+
214+
let decoder = NSKeyedUnarchiver(forReadingWith: data)
215+
decoder.decodingFailurePolicy = .setErrorAndReturn
216+
unarchiverSetup(decoder)
217+
218+
let object = decoder.decodeObject(of: ValueType.self, forKey: NSKeyedArchiveRootObjectKey)
219+
220+
XCTAssertNil(decoder.error)
221+
if let object = object {
222+
XCTAssertTrue(matchingWith(object, original), "The fixture with identifier '\(identifier)' failed to match after an in-memory roundtrip.")
223+
} else {
224+
XCTFail("The fixture with identifier '\(identifier)' failed to decode after an in-memory roundtrip.")
225+
}
226+
}
227+
228+
func assertValueRoundtripsInCoder(secureCoding: Bool, matchingWith: (ValueType, ValueType) -> Bool = { $0 == $1 }) throws {
229+
try assertValueRoundtripsInCoder(settingUpArchiverWith: { (archiver) in
230+
archiver.requiresSecureCoding = secureCoding
231+
}, unarchiverWith: { (unarchiver) in
232+
unarchiver.requiresSecureCoding = secureCoding
233+
}, matchingWith: matchingWith)
234+
}
194235
}
195236

196237
/// Test that the elements of `instances` satisfy the semantic

0 commit comments

Comments
 (0)