Skip to content

Commit fbc87b6

Browse files
committed
Parity: NSCoding stragglers: NSMeasurement
- Add implementation and fixture tests. - NSUnitConverterReciprocal may be decoded from Darwin archives; make sure its name matches.
1 parent b3c96d4 commit fbc87b6

9 files changed

+92
-20
lines changed

Foundation/NSMeasurement.swift

+32-2
Original file line numberDiff line numberDiff line change
@@ -81,9 +81,39 @@ open class NSMeasurement : NSObject, NSCopying, NSSecureCoding {
8181

8282
open class var supportsSecureCoding: Bool { return true }
8383

84-
open func encode(with aCoder: NSCoder) { NSUnimplemented() }
84+
private enum NSCodingKeys {
85+
static let value = "NS.value"
86+
static let unit = "NS.unit"
87+
}
8588

86-
public required init?(coder aDecoder: NSCoder) { NSUnimplemented() }
89+
public required init?(coder aDecoder: NSCoder) {
90+
let value = aDecoder.decodeDouble(forKey: NSCodingKeys.value)
91+
guard let unit = aDecoder.decodeObject(of: Unit.self, forKey: NSCodingKeys.unit) else {
92+
aDecoder.failWithError(NSError(domain: NSCocoaErrorDomain, code: CocoaError.coderReadCorrupt.rawValue, userInfo: [NSLocalizedDescriptionKey: "Unit class object has been corrupted!"]))
93+
return nil
94+
}
95+
96+
self.doubleValue = value
97+
self.unit = unit
98+
}
99+
100+
open func encode(with aCoder: NSCoder) {
101+
guard aCoder.allowsKeyedCoding else {
102+
fatalError("NSMeasurement cannot be encoded by non-keyed archivers")
103+
}
104+
105+
aCoder.encode(doubleValue, forKey: "NS.value")
106+
aCoder.encode(unit, forKey: "NS.unit")
107+
}
108+
109+
open override func isEqual(_ object: Any?) -> Bool {
110+
guard let measurement = object as? NSMeasurement else { return false }
111+
return measurement.unit.isEqual(self.unit) && doubleValue == measurement.doubleValue
112+
}
113+
114+
open override var hash: Int {
115+
return Int(doubleValue) ^ unit.hash
116+
}
87117
}
88118

89119
extension NSMeasurement : _StructTypeBridgeable {

Foundation/Unit.swift

+12-11
Original file line numberDiff line numberDiff line change
@@ -94,43 +94,44 @@ open class UnitConverterLinear : UnitConverter, NSSecureCoding {
9494
}
9595
}
9696

97-
private class UnitConverterReciprocal : UnitConverter, NSSecureCoding {
97+
// This must be named with a NS prefix because it can be sometimes encoded by Darwin, and we need to match the name in the archive.
98+
internal class NSUnitConverterReciprocal : UnitConverter, NSSecureCoding {
9899

99100

100101
private var reciprocal: Double
101102

102103

103-
fileprivate init(reciprocal: Double) {
104+
init(reciprocal: Double) {
104105
self.reciprocal = reciprocal
105106
}
106107

107-
fileprivate override func baseUnitValue(fromValue value: Double) -> Double {
108+
override func baseUnitValue(fromValue value: Double) -> Double {
108109
return reciprocal / value
109110
}
110111

111-
fileprivate override func value(fromBaseUnitValue baseUnitValue: Double) -> Double {
112+
override func value(fromBaseUnitValue baseUnitValue: Double) -> Double {
112113
return reciprocal / baseUnitValue
113114
}
114115

115-
fileprivate required convenience init?(coder aDecoder: NSCoder) {
116+
required convenience init?(coder aDecoder: NSCoder) {
116117
guard aDecoder.allowsKeyedCoding else {
117118
preconditionFailure("Unkeyed coding is unsupported.")
118119
}
119120
let reciprocal = aDecoder.decodeDouble(forKey: "NS.reciprocal")
120121
self.init(reciprocal: reciprocal)
121122
}
122123

123-
fileprivate func encode(with aCoder: NSCoder) {
124+
func encode(with aCoder: NSCoder) {
124125
guard aCoder.allowsKeyedCoding else {
125126
preconditionFailure("Unkeyed coding is unsupported.")
126127
}
127128
aCoder.encode(self.reciprocal, forKey:"NS.reciprocal")
128129
}
129130

130-
fileprivate static var supportsSecureCoding: Bool { return true }
131+
static var supportsSecureCoding: Bool { return true }
131132

132133
open override func isEqual(_ object: Any?) -> Bool {
133-
guard let other = object as? UnitConverterReciprocal else {
134+
guard let other = object as? NSUnitConverterReciprocal else {
134135
return false
135136
}
136137

@@ -215,8 +216,8 @@ open class Dimension : Unit {
215216
preconditionFailure("Unkeyed coding is unsupported.")
216217
}
217218
guard
218-
let symbol = aDecoder.decodeObject(forKey: "NS.symbol") as? String,
219-
let converter = aDecoder.decodeObject(forKey: "NS.converter") as? UnitConverter
219+
let symbol = aDecoder.decodeObject(of: NSString.self, forKey: "NS.symbol")?._swiftObject,
220+
let converter = aDecoder.decodeObject(of: [UnitConverterLinear.self, NSUnitConverterReciprocal.self], forKey: "NS.converter") as? UnitConverter
220221
else { return nil }
221222
self.converter = converter
222223
super.init(symbol: symbol)
@@ -1168,7 +1169,7 @@ public final class UnitFuelEfficiency : Dimension {
11681169
}
11691170

11701171
private convenience init(symbol: String, reciprocal: Double) {
1171-
self.init(symbol: symbol, converter: UnitConverterReciprocal(reciprocal: reciprocal))
1172+
self.init(symbol: symbol, converter: NSUnitConverterReciprocal(reciprocal: reciprocal))
11721173
}
11731174

11741175
public class var litersPer100Kilometers: UnitFuelEfficiency {

TestFoundation/FixtureValues.swift

+23
Original file line numberDiff line numberDiff line change
@@ -301,6 +301,25 @@ enum Fixtures {
301301
return NSMutableOrderedSet()
302302
}
303303

304+
// ===== NSMeasurement =====
305+
306+
static let zeroMeasurement = TypedFixture<NSMeasurement>("NSMeasurement-Zero") {
307+
let noUnit = Unit(symbol: "")
308+
return NSMeasurement(doubleValue: 0, unit: noUnit)
309+
}
310+
311+
static let lengthMeasurement = TypedFixture<NSMeasurement>("NSMeasurement-Length") {
312+
return NSMeasurement(doubleValue: 45, unit: UnitLength.miles)
313+
}
314+
315+
static let frequencyMeasurement = TypedFixture<NSMeasurement>("NSMeasurement-Frequency") {
316+
return NSMeasurement(doubleValue: 1400, unit: UnitFrequency.megahertz)
317+
}
318+
319+
static let angleMeasurement = TypedFixture<NSMeasurement>("NSMeasurement-Angle") {
320+
return NSMeasurement(doubleValue: 90, unit: UnitAngle.degrees)
321+
}
322+
304323
// ===== Fixture list =====
305324

306325
static let _listOfAllFixtures: [AnyFixture] = [
@@ -341,6 +360,10 @@ enum Fixtures {
341360
AnyFixture(Fixtures.orderedSetEmpty),
342361
AnyFixture(Fixtures.mutableOrderedSetOfNumbers),
343362
AnyFixture(Fixtures.mutableOrderedSetEmpty),
363+
AnyFixture(Fixtures.zeroMeasurement),
364+
AnyFixture(Fixtures.lengthMeasurement),
365+
AnyFixture(Fixtures.frequencyMeasurement),
366+
AnyFixture(Fixtures.angleMeasurement),
344367
]
345368

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

TestFoundation/TestMeasurement.swift

+24-6
Original file line numberDiff line numberDiff line change
@@ -23,12 +23,6 @@ class CustomUnit: Unit {
2323
#endif
2424

2525
class TestMeasurement: XCTestCase {
26-
static var allTests: [(String, (TestMeasurement) -> () throws -> Void)] {
27-
return [
28-
("testHashing", testHashing),
29-
]
30-
}
31-
3226
func testHashing() {
3327
let lengths: [[Measurement<UnitLength>]] = [
3428
[
@@ -75,5 +69,29 @@ class TestMeasurement: XCTestCase {
7569
checkHashable(custom, equalityOracle: { $0 == $1 })
7670
#endif
7771
}
72+
73+
let fixtures = [
74+
Fixtures.zeroMeasurement,
75+
Fixtures.lengthMeasurement,
76+
Fixtures.frequencyMeasurement,
77+
Fixtures.angleMeasurement,
78+
]
7879

80+
func testCodingRoundtrip() throws {
81+
for fixture in fixtures {
82+
try fixture.assertValueRoundtripsInCoder()
83+
}
84+
}
85+
86+
func testLoadedValuesMatch() throws {
87+
for fixture in fixtures {
88+
try fixture.assertLoadedValuesMatch()
89+
}
90+
}
91+
92+
static let allTests = [
93+
("testHashing", testHashing),
94+
("testCodingRoundtrip", testCodingRoundtrip),
95+
("testLoadedValuesMatch", testLoadedValuesMatch),
96+
]
7997
}

TestFoundation/TestUnitConverter.swift

+1-1
Original file line numberDiff line numberDiff line change
@@ -273,7 +273,7 @@ class TestUnitConverter: XCTestCase {
273273
XCTAssertNotEqual(u1, u4)
274274
XCTAssertNotEqual(u4, u1)
275275

276-
// Cannot test UnitConverterReciprocal due to no support for @testable import.
276+
// Cannot test NSUnitConverterReciprocal due to no support for @testable import.
277277
}
278278

279279
}

0 commit comments

Comments
 (0)