2

I am trying to decode a JSON from URL but my code can not decode this JSON.

This is the json: 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: json data

5
  • Again check my last edit Commented Sep 25, 2022 at 19:31
  • Btw don't paste screen shots, paste your code/json string. Nobody will type it to test your code. Commented Sep 25, 2022 at 19:32
  • I have edited with your code but I am getting this error: Type 'Formatter' has no member 'iso8601' Commented Sep 25, 2022 at 19:39
  • Have you added the custom formatters I have posted below? Commented Sep 25, 2022 at 19:42
  • Please read the whole post throughly Commented Sep 25, 2022 at 19:42

1 Answer 1

1

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)
}
Sign up to request clarification or add additional context in comments.

8 Comments

I have implemented these extensions but it does not work,
I had to make some changes. Your date format doesnt have the timezone. Check my last edit. Make sure your date string time zone is UTC otherwise remove those lines formatter.timeZone = TimeZone(secondsFromGMT: 0) from the dateFormatters.
I implemented your code but I am receiving the error bellow : Type 'Formatter' has no member 'iso8601'
This means you haven’t added the necessary code to your project. Or you need to declare them as public.
it is working so good, I am so thank with you.
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.