From 26eddf20cc7b2095dfcbf883fcbaeeda19cb11f4 Mon Sep 17 00:00:00 2001 From: Alsey Coleman Miller Date: Sat, 21 Dec 2019 22:57:42 -0500 Subject: [PATCH 1/7] Added `TLVDateFormat` --- Sources/Format.swift | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/Sources/Format.swift b/Sources/Format.swift index 5b68fbc..35b30ea 100644 --- a/Sources/Format.swift +++ b/Sources/Format.swift @@ -20,9 +20,30 @@ public enum TLVUUIDFormat: Equatable, Hashable { case string } +/// TLV Date Encoding Format +public enum TLVDateFormat: Equatable, Hashable { + + /// The strategy that encodes dates in terms of seconds since midnight UTC on January 1, 1970. + case secondsSince1970 + + /// The strategy that encodes dates in terms of milliseconds since midnight UTC on January 1, 1970. + case millisecondsSince1970 + + /// The strategy that formats dates according to the ISO 8601 and RFC 3339 standards. + case iso8601 + + /// The strategy that defers formatting settings to a supplied date formatter. + case formatted(DateFormatter) + + /// The strategy that formats custom dates by calling a user-defined function. + case custom((Date, Encoder) -> Void) +} + internal struct TLVOptions { let numericFormat: TLVNumericFormat let uuidFormat: TLVUUIDFormat + + let dateFormat: TLVDateFormat } From 13723240dff7781e00a6de54ac10bc68f1fa3f37 Mon Sep 17 00:00:00 2001 From: Alsey Coleman Miller Date: Sun, 22 Dec 2019 00:08:13 -0500 Subject: [PATCH 2/7] Added Date encoding --- Sources/Encoder.swift | 96 ++++++++++++++++++++++++++++++++++--------- Sources/Format.swift | 16 ++++---- 2 files changed, 84 insertions(+), 28 deletions(-) diff --git a/Sources/Encoder.swift b/Sources/Encoder.swift index e017515..f0552ee 100644 --- a/Sources/Encoder.swift +++ b/Sources/Encoder.swift @@ -25,6 +25,9 @@ public struct TLVEncoder { /// Format for UUID values. public var uuidFormat: TLVUUIDFormat = .bytes + /// Format for Date values. + public var dateFormat: TLVDateFormat = .secondsSince1970 + // MARK: - Initialization public init() { } @@ -37,7 +40,8 @@ public struct TLVEncoder { let options = Encoder.Options( numericFormat: numericFormat, - uuidFormat: uuidFormat + uuidFormat: uuidFormat, + dateFormat: dateFormat ) let encoder = Encoder(userInfo: userInfo, log: log, options: options) try value.encode(to: encoder) @@ -151,12 +155,12 @@ internal extension TLVEncoder.Encoder { internal extension TLVEncoder.Encoder { @inline(__always) - func box (_ value: T) -> Data { + func box (_ value: T) -> Data { return value.tlvData } @inline(__always) - func boxNumeric (_ value: T) -> Data { + func boxNumeric (_ value: T) -> Data { let numericValue: T switch options.numericFormat { @@ -168,12 +172,27 @@ internal extension TLVEncoder.Encoder { return box(numericValue) } + @inline(__always) + func boxDouble(_ double: Double) -> Data { + return boxNumeric(double.bitPattern) + } + + @inline(__always) + func boxFloat(_ float: Float) -> Data { + return boxNumeric(float.bitPattern) + } + func boxEncodable (_ value: T) throws -> Data { + // should not call this function + assert(value as? TLVRawEncodable == nil, "Should not call this method for native types") + if let data = value as? Data { return data } else if let uuid = value as? UUID { return boxUUID(uuid) + } else if let date = value as? Date { + return boxDate(date) } else if let tlvEncodable = value as? TLVEncodable { return tlvEncodable.tlvData } else { @@ -183,8 +202,12 @@ internal extension TLVEncoder.Encoder { return nestedContainer.data } } +} + +private extension TLVEncoder.Encoder { - private func boxUUID(_ uuid: UUID) -> Data { + func boxUUID(_ uuid: UUID) -> Data { + switch options.uuidFormat { case .bytes: return Data(uuid) @@ -192,6 +215,34 @@ internal extension TLVEncoder.Encoder { return uuid.uuidString.tlvData } } + + func boxDate(_ date: Date) -> Data { + + switch options.dateFormat { + case .secondsSince1970: + return boxDouble(date.timeIntervalSince1970) + case .millisecondsSince1970: + return boxDouble(date.timeIntervalSince1970 * 1000) + case .iso8601: + if #available(OSX 10.12, *) { + return box(TLVEncoder.Encoder.iso8601Formatter.string(from: date)) + } else { + fatalError("ISO8601DateFormatter is unavailable on this platform.") + } + case let .formatted(formatter): + return box(formatter.string(from: date)) + } + } +} + +internal extension TLVEncoder.Encoder { + + @available(macOS 10.12, iOS 10.0, watchOS 3.0, tvOS 10.0, *) + static let iso8601Formatter: ISO8601DateFormatter = { + let formatter = ISO8601DateFormatter() + formatter.formatOptions = .withInternetDateTime + return formatter + }() } // MARK: - Stack @@ -395,7 +446,7 @@ internal final class TLVKeyedContainer : KeyedEncodingContainerP // MARK: - Private Methods - private func encodeNumeric (_ value: T, forKey key: K) throws { + private func encodeNumeric (_ value: T, forKey key: K) throws { self.encoder.codingPath.append(key) defer { self.encoder.codingPath.removeLast() } @@ -403,7 +454,7 @@ internal final class TLVKeyedContainer : KeyedEncodingContainerP try setValue(value, data: data, for: key) } - private func encodeTLV (_ value: T, forKey key: K) throws { + private func encodeTLV (_ value: T, forKey key: K) throws { self.encoder.codingPath.append(key) defer { self.encoder.codingPath.removeLast() } @@ -590,92 +641,97 @@ internal final class TLVUnkeyedEncodingContainer: UnkeyedEncodingContainer { // MARK: - Data Types -private extension TLVEncodable { +/// Private protocol for encoding TLV values into raw data. +internal protocol TLVRawEncodable { + + var tlvData: Data { get } +} + +private extension TLVRawEncodable { var copyingBytes: Data { - return withUnsafePointer(to: self, { Data(bytes: $0, count: MemoryLayout.size) }) } } -extension UInt8: TLVEncodable { +extension UInt8: TLVRawEncodable { public var tlvData: Data { return copyingBytes } } -extension UInt16: TLVEncodable { +extension UInt16: TLVRawEncodable { public var tlvData: Data { return copyingBytes } } -extension UInt32: TLVEncodable { +extension UInt32: TLVRawEncodable { public var tlvData: Data { return copyingBytes } } -extension UInt64: TLVEncodable { +extension UInt64: TLVRawEncodable { public var tlvData: Data { return copyingBytes } } -extension Int8: TLVEncodable { +extension Int8: TLVRawEncodable { public var tlvData: Data { return copyingBytes } } -extension Int16: TLVEncodable { +extension Int16: TLVRawEncodable { public var tlvData: Data { return copyingBytes } } -extension Int32: TLVEncodable { +extension Int32: TLVRawEncodable { public var tlvData: Data { return copyingBytes } } -extension Int64: TLVEncodable { +extension Int64: TLVRawEncodable { public var tlvData: Data { return copyingBytes } } -extension Float: TLVEncodable { +extension Float: TLVRawEncodable { public var tlvData: Data { return bitPattern.copyingBytes } } -extension Double: TLVEncodable { +extension Double: TLVRawEncodable { public var tlvData: Data { return bitPattern.copyingBytes } } -extension Bool: TLVEncodable { +extension Bool: TLVRawEncodable { public var tlvData: Data { return UInt8(self ? 1 : 0).copyingBytes } } -extension String: TLVEncodable { +extension String: TLVRawEncodable { public var tlvData: Data { return Data(self.utf8) diff --git a/Sources/Format.swift b/Sources/Format.swift index 35b30ea..5a91a07 100644 --- a/Sources/Format.swift +++ b/Sources/Format.swift @@ -6,6 +6,8 @@ // Copyright © 2019 PureSwift. All rights reserved. // +import Foundation + /// TLV Numeric Encoding Format public enum TLVNumericFormat: Equatable, Hashable { @@ -21,22 +23,20 @@ public enum TLVUUIDFormat: Equatable, Hashable { } /// TLV Date Encoding Format -public enum TLVDateFormat: Equatable, Hashable { +public enum TLVDateFormat: Equatable { - /// The strategy that encodes dates in terms of seconds since midnight UTC on January 1, 1970. + /// Encodes dates in terms of seconds since midnight UTC on January 1, 1970. case secondsSince1970 - /// The strategy that encodes dates in terms of milliseconds since midnight UTC on January 1, 1970. + /// Encodes dates in terms of milliseconds since midnight UTC on January 1, 1970. case millisecondsSince1970 - /// The strategy that formats dates according to the ISO 8601 and RFC 3339 standards. + /// Formats dates according to the ISO 8601 and RFC 3339 standards. + @available(macOS 10.12, iOS 10.0, watchOS 3.0, tvOS 10.0, *) case iso8601 - /// The strategy that defers formatting settings to a supplied date formatter. + /// Defers formatting settings to a supplied date formatter. case formatted(DateFormatter) - - /// The strategy that formats custom dates by calling a user-defined function. - case custom((Date, Encoder) -> Void) } internal struct TLVOptions { From d980f68a881af08c2368fdb997168705c11d5b21 Mon Sep 17 00:00:00 2001 From: Alsey Coleman Miller Date: Sun, 22 Dec 2019 04:22:01 -0500 Subject: [PATCH 3/7] Added Date decoding --- Sources/Decoder.swift | 84 ++++++++++++++++++++++++++++++++++--------- Sources/Encoder.swift | 22 ++++-------- Sources/Format.swift | 24 +++++++++++++ 3 files changed, 99 insertions(+), 31 deletions(-) diff --git a/Sources/Decoder.swift b/Sources/Decoder.swift index c6936e7..3fba4b6 100644 --- a/Sources/Decoder.swift +++ b/Sources/Decoder.swift @@ -25,6 +25,9 @@ public struct TLVDecoder { /// Format for UUID values. public var uuidFormat: TLVUUIDFormat = .bytes + /// Format for Date values. + public var dateFormat: TLVDateFormat = .secondsSince1970 + // MARK: - Initialization public init() { } @@ -39,7 +42,8 @@ public struct TLVDecoder { let options = Decoder.Options( numericFormat: numericFormat, - uuidFormat: uuidFormat + uuidFormat: uuidFormat, + dateFormat: dateFormat ) let decoder = Decoder(referencing: .items(items), @@ -226,7 +230,7 @@ internal extension TLVDecoder.Decoder { internal extension TLVDecoder.Decoder { - func unbox (_ data: Data, as type: T.Type) throws -> T { + func unbox (_ data: Data, as type: T.Type) throws -> T { guard let value = T.init(tlvData: data) else { @@ -236,7 +240,7 @@ internal extension TLVDecoder.Decoder { return value } - func unboxNumeric (_ data: Data, as type: T.Type) throws -> T { + func unboxNumeric (_ data: Data, as type: T.Type) throws -> T { var numericValue = try unbox(data, as: type) switch options.numericFormat { @@ -248,6 +252,16 @@ internal extension TLVDecoder.Decoder { return numericValue } + func unboxDouble(_ data: Data) throws -> Double { + let bitPattern = try unboxNumeric(data, as: UInt64.self) + return Double(bitPattern: bitPattern) + } + + func unboxFloat(_ data: Data) throws -> Float { + let bitPattern = try unboxNumeric(data, as: UInt32.self) + return Float(bitPattern: bitPattern) + } + /// Attempt to decode native value to expected type. func unboxDecodable (_ item: TLVItem, as type: T.Type) throws -> T { @@ -256,6 +270,8 @@ internal extension TLVDecoder.Decoder { return item.value as! T // In this case T is Data } else if type == UUID.self { return try unboxUUID(item.value) as! T + } else if type == Date.self { + return try unboxDate(item.value) as! T } else if let tlvCodable = type as? TLVCodable.Type { guard let value = tlvCodable.init(tlvData: item.value) else { throw DecodingError.dataCorrupted(DecodingError.Context(codingPath: codingPath, debugDescription: "Invalid data for \(String(reflecting: type))")) @@ -269,8 +285,11 @@ internal extension TLVDecoder.Decoder { return decoded } } +} + +private extension TLVDecoder.Decoder { - private func unboxUUID(_ data: Data) throws -> UUID { + func unboxUUID(_ data: Data) throws -> UUID { switch options.uuidFormat { case .bytes: @@ -288,6 +307,33 @@ internal extension TLVDecoder.Decoder { return uuid } } + + func unboxDate(_ data: Data) throws -> Date { + + switch options.dateFormat { + case .secondsSince1970: + let timeInterval = try unboxDouble(data) + return Date(timeIntervalSince1970: timeInterval) + case .millisecondsSince1970: + let timeInterval = try unboxDouble(data) + return Date(timeIntervalSince1970: timeInterval / 1000) + case .iso8601: + guard #available(OSX 10.12, *) + else { fatalError("ISO8601DateFormatter is unavailable on this platform.") } + return try unboxDate(data, using: TLVDateFormat.iso8601Formatter) + case let .formatted(formatter): + return try unboxDate(data, using: formatter) + } + } + + @inline(__always) + func unboxDate (_ data: Data, using formatter: T) throws -> Date { + let string = try unbox(data, as: String.self) + guard let date = formatter.date(from: string) else { + throw DecodingError.dataCorrupted(DecodingError.Context(codingPath: codingPath, debugDescription: "Invalid Date string \(string)")) + } + return date + } } // MARK: - Stack @@ -489,12 +535,12 @@ internal struct TLVKeyedDecodingContainer : KeyedDecodingContaine // MARK: Private Methods /// Decode native value type from TLV data. - private func decodeTLV (_ type: T.Type, forKey key: Key) throws -> T { + private func decodeTLV (_ type: T.Type, forKey key: Key) throws -> T { return try self.value(for: key, type: type) { try decoder.unbox($0.value, as: type) } } - private func decodeNumeric (_ type: T.Type, forKey key: Key) throws -> T { + private func decodeNumeric (_ type: T.Type, forKey key: Key) throws -> T { return try self.value(for: key, type: type) { try decoder.unboxNumeric($0.value, as: type) } } @@ -788,7 +834,13 @@ internal extension TLVUnkeyedDecodingContainer { // MARK: - Decodable Types -extension String: TLVDecodable { +/// Private protocol for decoding TLV values into raw data. +internal protocol TLVRawDecodable { + + init?(tlvData data: Data) +} + +extension String: TLVRawDecodable { public init?(tlvData data: Data) { @@ -796,7 +848,7 @@ extension String: TLVDecodable { } } -extension Bool: TLVDecodable { +extension Bool: TLVRawDecodable { public init?(tlvData data: Data) { @@ -807,7 +859,7 @@ extension Bool: TLVDecodable { } } -extension UInt8: TLVDecodable { +extension UInt8: TLVRawDecodable { public init?(tlvData data: Data) { @@ -818,7 +870,7 @@ extension UInt8: TLVDecodable { } } -extension UInt16: TLVDecodable { +extension UInt16: TLVRawDecodable { public init?(tlvData data: Data) { @@ -829,7 +881,7 @@ extension UInt16: TLVDecodable { } } -extension UInt32: TLVDecodable { +extension UInt32: TLVRawDecodable { public init?(tlvData data: Data) { @@ -840,7 +892,7 @@ extension UInt32: TLVDecodable { } } -extension UInt64: TLVDecodable { +extension UInt64: TLVRawDecodable { public init?(tlvData data: Data) { @@ -851,7 +903,7 @@ extension UInt64: TLVDecodable { } } -extension Int8: TLVDecodable { +extension Int8: TLVRawDecodable { public init?(tlvData data: Data) { @@ -862,7 +914,7 @@ extension Int8: TLVDecodable { } } -extension Int16: TLVDecodable { +extension Int16: TLVRawDecodable { public init?(tlvData data: Data) { @@ -873,7 +925,7 @@ extension Int16: TLVDecodable { } } -extension Int32: TLVDecodable { +extension Int32: TLVRawDecodable { public init?(tlvData data: Data) { @@ -884,7 +936,7 @@ extension Int32: TLVDecodable { } } -extension Int64: TLVDecodable { +extension Int64: TLVRawDecodable { public init?(tlvData data: Data) { diff --git a/Sources/Encoder.swift b/Sources/Encoder.swift index f0552ee..4cd8e64 100644 --- a/Sources/Encoder.swift +++ b/Sources/Encoder.swift @@ -224,25 +224,17 @@ private extension TLVEncoder.Encoder { case .millisecondsSince1970: return boxDouble(date.timeIntervalSince1970 * 1000) case .iso8601: - if #available(OSX 10.12, *) { - return box(TLVEncoder.Encoder.iso8601Formatter.string(from: date)) - } else { - fatalError("ISO8601DateFormatter is unavailable on this platform.") - } + guard #available(OSX 10.12, *) + else { fatalError("ISO8601DateFormatter is unavailable on this platform.") } + return boxDate(date, using: TLVDateFormat.iso8601Formatter) case let .formatted(formatter): - return box(formatter.string(from: date)) + return boxDate(date, using: formatter) } } -} - -internal extension TLVEncoder.Encoder { - @available(macOS 10.12, iOS 10.0, watchOS 3.0, tvOS 10.0, *) - static let iso8601Formatter: ISO8601DateFormatter = { - let formatter = ISO8601DateFormatter() - formatter.formatOptions = .withInternetDateTime - return formatter - }() + func boxDate (_ date: Date, using formatter: T) -> Data { + return box(formatter.string(from: date)) + } } // MARK: - Stack diff --git a/Sources/Format.swift b/Sources/Format.swift index 5a91a07..21fefd5 100644 --- a/Sources/Format.swift +++ b/Sources/Format.swift @@ -47,3 +47,27 @@ internal struct TLVOptions { let dateFormat: TLVDateFormat } + +// MARK: - Formatters + +internal extension TLVDateFormat { + + @available(macOS 10.12, iOS 10.0, watchOS 3.0, tvOS 10.0, *) + static let iso8601Formatter: ISO8601DateFormatter = { + let formatter = ISO8601DateFormatter() + formatter.formatOptions = .withInternetDateTime + return formatter + }() +} + +internal protocol DateFormatterProtocol: class { + + func string(from date: Date) -> String + + func date(from string: String) -> Date? +} + +extension DateFormatter: DateFormatterProtocol { } + +@available(macOS 10.12, iOS 10.0, watchOS 3.0, tvOS 10.0, *) +extension ISO8601DateFormatter: DateFormatterProtocol { } From d65439248bd8ea82fa851182e9800d0a5b4f4f4b Mon Sep 17 00:00:00 2001 From: Alsey Coleman Miller Date: Sun, 22 Dec 2019 04:22:23 -0500 Subject: [PATCH 4/7] Updated `TLVCodable` --- Sources/TLVCodable.swift | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Sources/TLVCodable.swift b/Sources/TLVCodable.swift index 6a9d1ce..b8290b8 100644 --- a/Sources/TLVCodable.swift +++ b/Sources/TLVCodable.swift @@ -12,12 +12,12 @@ import Foundation public typealias TLVCodable = TLVEncodable & TLVDecodable /// TLV Decodable type -public protocol TLVDecodable { +public protocol TLVDecodable: Decodable { init?(tlvData: Data) } -public protocol TLVEncodable { +public protocol TLVEncodable: Encodable { var tlvData: Data { get } } From 945d9cf2d4ba85a9e27d5c631d3762a6138e4377 Mon Sep 17 00:00:00 2001 From: Alsey Coleman Miller Date: Sun, 22 Dec 2019 04:55:09 -0500 Subject: [PATCH 5/7] Updated unit tests --- Tests/TLVCodingTests/TLVCodingTests.swift | 57 +++++++++++++++++++++-- 1 file changed, 54 insertions(+), 3 deletions(-) diff --git a/Tests/TLVCodingTests/TLVCodingTests.swift b/Tests/TLVCodingTests/TLVCodingTests.swift index 57b120f..39f0b6e 100644 --- a/Tests/TLVCodingTests/TLVCodingTests.swift +++ b/Tests/TLVCodingTests/TLVCodingTests.swift @@ -190,8 +190,7 @@ final class TLVCodingTests: XCTestCase { Data([ 0, 32, 0, 1, 0, 1, 27, 0, 16, 184, 61, 214, 244, 164, 41, 65, 179, 148, 90, 62, 14, 229, 145, 92, 161, 1, 7, 86, 97, 108, 117, 101, 32, 49, 1, 32, 0, 1, 0, 1, 27, 0, 16, 184, 61, 214, 244, 164, 41, 65, 179, 148, 90, 62, 14, 229, 145, 92, 162, 1, 7, 86, 97, 108, 117, 101, 32, 50, - 2, 50, 0, 1, 1, 1, 45, 0, 16, 184, 61, 214, 244, 164, 41, 65, 179, 148, 90, 62, 14, 229, 145, 92, 163, 1, 15, 80, 101, 110, 100, 105, 110, 103, 32, 86, 97, 108, 117, 101, 32, 49, 2, 8, 0, 0, 0, 127, 195, 99, 45, 66 - ]) + 2, 50, 0, 1, 1, 1, 45, 0, 16, 184, 61, 214, 244, 164, 41, 65, 179, 148, 90, 62, 14, 229, 145, 92, 163, 1, 15, 80, 101, 110, 100, 105, 110, 103, 32, 86, 97, 108, 117, 101, 32, 49, 2, 8, 0, 0, 0, 16, 99, 216, 45, 66]) ) } @@ -216,7 +215,8 @@ final class TLVCodingTests: XCTestCase { let value = CustomEncodable( data: nil, uuid: UUID(), - number: nil + number: nil, + date: nil ) var encodedData = Data() @@ -243,6 +243,56 @@ final class TLVCodingTests: XCTestCase { } } } + + func testDate() { + + let dateFormatter = DateFormatter() + dateFormatter.locale = Locale(identifier: "en_US_POSIX") + dateFormatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ss.SSS'Z'" + dateFormatter.calendar = Calendar(identifier: .gregorian) + dateFormatter.timeZone = TimeZone(secondsFromGMT: 0) + + var formats: [TLVDateFormat] = [.secondsSince1970, .millisecondsSince1970, .formatted(dateFormatter)] + + if #available(macOS 10.12, iOS 10.0, watchOS 3.0, tvOS 10.0, *) { + formats.append(.iso8601) + } + + let date = Date(timeIntervalSince1970: 60 * 60 * 24 * 365) + + for format in formats { + + let value = CustomEncodable( + data: nil, + uuid: nil, + number: nil, + date: date + ) + + var encodedData = Data() + var encoder = TLVEncoder() + encoder.dateFormat = format + encoder.log = { print("Encoder:", $0) } + do { + encodedData = try encoder.encode(value) + } catch { + dump(error) + XCTFail("Could not encode \(value)") + return + } + + var decoder = TLVDecoder() + decoder.dateFormat = format + decoder.log = { print("Decoder:", $0) } + do { + let decodedValue = try decoder.decode(CustomEncodable.self, from: encodedData) + XCTAssertEqual(decodedValue, value) + } catch { + dump(error) + XCTFail("Could not decode \(value)") + } + } + } } // MARK: - Supporting Types @@ -358,6 +408,7 @@ public struct CustomEncodable: Codable, Equatable { public var data: Data? public var uuid: UUID? public var number: TLVCodableNumber? + public var date: Date? } public struct DeviceInformation: Equatable, Codable { From 6f1a81c60b3d6080af3fe5c89d61a110a0fc72b3 Mon Sep 17 00:00:00 2001 From: Alsey Coleman Miller Date: Sun, 22 Dec 2019 04:55:36 -0500 Subject: [PATCH 6/7] Removed assertion --- Sources/Encoder.swift | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/Sources/Encoder.swift b/Sources/Encoder.swift index 4cd8e64..cc6c902 100644 --- a/Sources/Encoder.swift +++ b/Sources/Encoder.swift @@ -184,9 +184,6 @@ internal extension TLVEncoder.Encoder { func boxEncodable (_ value: T) throws -> Data { - // should not call this function - assert(value as? TLVRawEncodable == nil, "Should not call this method for native types") - if let data = value as? Data { return data } else if let uuid = value as? UUID { @@ -502,9 +499,9 @@ internal final class TLVSingleValueEncodingContainer: SingleValueEncodingContain func encode(_ value: String) throws { write(encoder.box(value)) } - func encode(_ value: Double) throws { write(encoder.boxNumeric(value.bitPattern)) } + func encode(_ value: Double) throws { write(encoder.boxDouble(value)) } - func encode(_ value: Float) throws { write(encoder.boxNumeric(value.bitPattern)) } + func encode(_ value: Float) throws { write(encoder.boxFloat(value)) } func encode(_ value: Int) throws { write(encoder.boxNumeric(Int32(value))) } From b9960b8df8c645e91df79a4ecfe903e8c4b52aab Mon Sep 17 00:00:00 2001 From: Alsey Coleman Miller Date: Sun, 22 Dec 2019 05:03:48 -0500 Subject: [PATCH 7/7] Updated unit tests --- Tests/TLVCodingTests/TLVCodingTests.swift | 42 +++++++++++++++++++++-- 1 file changed, 40 insertions(+), 2 deletions(-) diff --git a/Tests/TLVCodingTests/TLVCodingTests.swift b/Tests/TLVCodingTests/TLVCodingTests.swift index 39f0b6e..a3b9804 100644 --- a/Tests/TLVCodingTests/TLVCodingTests.swift +++ b/Tests/TLVCodingTests/TLVCodingTests.swift @@ -12,10 +12,12 @@ import XCTest final class TLVCodingTests: XCTestCase { - static var allTests = [ + static let allTests = [ ("testCodable", testCodable), ("testCodingKeys", testCodingKeys), - ("testUUID", testUUID) + ("testUUID", testUUID), + ("testDate", testDate), + ("testDateSecondsSince1970", testDateSecondsSince1970) ] func testCodable() { @@ -293,6 +295,28 @@ final class TLVCodingTests: XCTestCase { } } } + + func testDateSecondsSince1970() { + + let date = Date(timeIntervalSince1970: 60 * 60 * 24 * 365) + + let value = Transaction( + id: UUID(), + date: date, + description: "Test" + ) + + let rawValue = TransactionRaw( + id: value.id, + date: value.date.timeIntervalSince1970, + description: value.description + ) + + var encoder = TLVEncoder() + encoder.dateFormat = .secondsSince1970 + encoder.log = { print("Encoder:", $0) } + XCTAssertEqual(try encoder.encode(value), try encoder.encode(rawValue)) + } } // MARK: - Supporting Types @@ -309,6 +333,20 @@ public enum Gender: UInt8, Codable { case female } +public struct Transaction: Equatable, Codable { + + public let id: UUID + public let date: Date + public let description: String +} + +public struct TransactionRaw: Equatable, Codable { + + public let id: UUID + public let date: Double + public let description: String +} + public struct ProvisioningState: Codable, Equatable { public var state: State