I am trying to decode a JSON from URL but my code can not decode this JSON.
and this is my model object :
struct User: Decodable, Identifiable {
let name: String
let birthdate: Date
let id: Int
}
and this is my fetch users code:
func fetchUsers() async throws -> [User]? {
let endPoint = EndPoint.users.rawValue
guard let url = URL(string: endPoint) else {
print("Invalid URL")
return nil
}
let urlRequest = URLRequest(url: url)
do {
let (json, _) = try await URLSession.shared.data(for: urlRequest)
return try JSONDecoder().decode([User].self, from: json)
} catch {
print(error)
}
}
// create user with async/await
}
when I debug I get this data result of URLSession:
CodePudding user response:
The issue there is in your JSONDecoder dateDecodingStrategy. The default strategy is called deferredToDate
which means it is expecting the timeIntervalSinceReferenceDate
. What you need is a custom dateFormat to parse your ISO8601 date format. Check this How to convert a date string with optional fractional seconds using Codable in Swift?.
Just create a custom DateFormatter iso8601 with and without fractional seconds:
extension Formatter {
static let iso8601withFractionalSeconds: DateFormatter = {
let formatter = DateFormatter()
formatter.calendar = Calendar(identifier: .iso8601)
formatter.locale = Locale(identifier: "en_US_POSIX")
formatter.timeZone = TimeZone(secondsFromGMT: 0)
formatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ss.SSS"
return formatter
}()
static let iso8601: DateFormatter = {
let formatter = DateFormatter()
formatter.calendar = Calendar(identifier: .iso8601)
formatter.locale = Locale(identifier: "en_US_POSIX")
formatter.timeZone = TimeZone(secondsFromGMT: 0)
formatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ss"
return formatter
}()
}
And a custom dateDecodingStrategy:
extension JSONDecoder.DateDecodingStrategy {
static let customISO8601 = custom {
let container = try $0.singleValueContainer()
let string = try container.decode(String.self)
if let date = Formatter.iso8601withFractionalSeconds.date(from: string) ?? Formatter.iso8601.date(from: string) {
return date
}
throw DecodingError.dataCorruptedError(in: container, debugDescription: "Invalid date: \(string)")
}
}
Now you can set your JSONDecoder dateDecodingStrategy properly:
let decoder = JSONDecoder()
decoder.dateDecodingStrategy = .customISO8601
And use that decoder to parse your JSON string. Note that if you use try? you are discarding the error which you should use to debug your issue:
return try decoder.decode([User].self, from: json)
} catch {
print(error)
}