I am trying to serialize a struct to a String using Swift 4's Encodable+JSONEncoder. The object can hold heterogenous values like String, Array, Date, Int etc.
The used approach works fine with the exception of Date.
JSONEncoder's dateEncodingStrategy
property is not having any effect.
Here is a snippet which reproduces the behaviour in Playground:
struct EncodableValue:Encodable {
var value: Encodable
init(_ value: Encodable) {
self.value = value
}
func encode(to encoder: Encoder) throws {
try value.encode(to: encoder)
}
}
struct Bar: Encodable, CustomStringConvertible {
let key: String?
let value: EncodableValue?
var description: String {
let encoder = JSONEncoder()
let dateFormatter = DateFormatter()
dateFormatter.dateFormat = "E, d MMM yyyy"
dateFormatter.locale = Locale(identifier: "en_US_POSIX")
encoder.dateEncodingStrategy = .formatted(dateFormatter)
let jsonData = try? encoder.encode(self)
return String(data: jsonData!, encoding: .utf8)!
}
}
let bar1 = Bar(key: "bar1", value: EncodableValue("12345"))
let bar2 = Bar(key: "bar2", value: EncodableValue(12345))
let bar3 = Bar(key: "bar3", value: EncodableValue(Date()))
print(String(describing: bar1))
print(String(describing: bar2))
print(String(describing: bar3))
Output:
"{"key":"bar1","value":"12345"}\n"
"{"key":"bar2","value":12345}\n"
"{"key":"bar3","value":539682026.06086397}\n"
For bar3 object: I'm expecting something like "{"key":"bar3","value":"Thurs, 3 Jan 1991"}\n"
, but it returns the date in the default .deferToDate strategy format.
##EDIT 1##
So I ran the same code in XCode 9 and it gives the expected output, i.e. correctly formats the date to string. I'm thinking 9.2 has a minor upgrade to Swift 4 which is breaking this feature. Not sure what to do next.
##EDIT 2##
As a temp remedy I'd used the following snippet before changing to @Hamish's approach using a closure.
struct EncodableValue:Encodable {
var value: Encodable
init(_ value: Encodable) {
self.value = value
}
func encode(to encoder: Encoder) throws {
if let date = value as? Date {
var container = encoder.singleValueContainer()
try container.encode(date)
}
else {
try value.encode(to: encoder)
}
}
}
EncodableValue
wrapper. – Raleigh