I have GeoJSON data from an API and it contains dates that are in ISO8601 format. I can decode them in SwiftUI as a string and manipulate them through a calculated field to get a version that is type Date but it's clumsy. I know the JSONDecoder supports date en/decoding options and I'd like the same behaviour.. similar to this:
let encoder = JSONEncoder()
encoder.dateEncodingStrategy = .iso8601
let decoder = JSONDecoder()
decoder.dateDecodingStrategy = .iso8601
I was thinking of maybe an extension to MKGeoJSONDecoder but I can't figure out how to even get started because of the need to be in the parsing flow.
Thoughts? Thanks
Thanks vadian, all of the data I wanted was in the properties attribute so there was really no value in using MKGeoJSONDecoder. I just switched to normal JSONDecoder and put a custom ISO8601 formatter to match my data and all works great.
In case anyone wants to reference, here is the playground code. You'd want to add a bunch of error checking of course. And thanks to Sarunw for the great info on the custom date decoding (https://sarunw.com/posts/how-to-parse-iso8601-date-in-swift/)
import Foundation
import MapKit
struct Response: Codable {
let features: [Feature]
}
struct Feature: Codable {
let properties: Properties
}
struct Properties: Codable {
let stnNamValue: String?
let dateTmValue: Date?
enum CodingKeys: String, CodingKey {
case stnNamValue = "stn_nam-value"
case dateTmValue = "date_tm-value"
}
}
Task {
// get the data from API
let url = URL(string: "https://api.weather.gc.ca/collections/swob-realtime/items?f=json&sortby=-date_tm-value&clim_id-value=5050919&limit=3")
let (data, _) = try await URLSession.shared.data(from: url!)
// create the decoder with custom ISO8601 formatter
let decoder = JSONDecoder()
let formatter = ISO8601DateFormatter()
formatter.formatOptions = [.withFullDate, .withFullTime,.withFractionalSeconds, .withTimeZone]
decoder.dateDecodingStrategy = .custom({ decoder in
let container = try decoder.singleValueContainer()
let dateString = try container.decode(String.self)
if let date = formatter.date(from: dateString) {return date}
throw DecodingError.dataCorruptedError(in: container, debugDescription: "Cannot decode date string \(dateString)")
})
// decode & print the results
let decodedResponse = try decoder.decode(Response.self, from: data)
decodedResponse.features.forEach {(feature) in
print("Station: \(feature.properties.stnNamValue ?? "<no value>"), Date: \(feature.properties.dateTmValue)")
}
}
Output is the date/times of the most recent surface weather observations for Flin Flon, Manitoba:
Station: FLIN FLON, Date: Optional(2022-02-16 12:22:00 +0000)
Station: FLIN FLON, Date: Optional(2022-02-16 12:21:00 +0000)
Station: FLIN FLON, Date: Optional(2022-02-16 12:20:00 +0000)