iosjsonswiftdecodablejsondecoder

Swift Decodable, is it possible to use JSONDecoder to parse JSON like this into my target object?


I have a WebAPI which returns JSON like the following: {"result":"true","message":"2023-07-01 16:09:02"}

I have a Decodable object defined like this:

struct ActionClock: Codable{
    var result: Bool? = false
    var message: String? = "invalid"
}

I have code that looks like the following and fails when attempting to decode the JSON.

NOTE: This is from a code sample so some of it I am unfamiliar with..

func loadTime() {
     guard let url = URL(string: "https://actionmobile.app/GetTime" ) 
     else {
            print("Invalid URL")
            return
       }
 let request = URLRequest(url: url)

 URLSession.shared.dataTask(with: request) { data, response, error in
  if let data = data {
     print("data: \(data) \(Date())")
     if let response = try? JSONDecoder().decode(ActionClock.self, from: data) {
      print("is this called?")
      DispatchQueue.main.async {
         self.atime = response
         print("in LOADTIME")
       }
      print("response: \(response)")
      return
    }
  }
  else{
     print("I failed")
    }
 }.resume() 
}

When this code runs, it prints the number of bytes in data and the date (successfully hits the webApi and retrieves the json), but the JSONDecoder.decode() call fails to parse my JSON.

Can you tell me:

  1. Is it possible to use the JSONDecoder to decode this particular JSON?
  2. Do I need to change something in my Codable class so it will parse correctly?
  3. Can you show me how to wrap the decode() call in a proper try...catch so I can see the exception that is thrown?

Solution

  • I altered the code to print the error (implemented do { try } ) and discovered that the JSONDecoder doesn't think it can parse the Boolean value returned in the original JSON because it believes it is a string (because the value is wrapped in double-quotes).

    Here's the error:

    typeMismatch(Swift.Bool, Swift.DecodingError.Context(codingPath: [CodingKeys(stringValue: "result", intValue: nil)], debugDescription: "Expected to decode Bool but found a string/data instead.", underlyingError: nil))

    I think that is incorrect really, because if you run JSONLint on my original JSON you will see the following:

    jsonlint valid json

    I changed the type in my original Codable object and everything worked as expected. I also made them non-nullable since I init the values.

    struct ActionClock: Codable{
        var result: String = "false"
        var message: String = "invalid"
    }
    

    Here's the updated code:

    func loadTime() {
        guard let url = URL(string: "https://actionmobile.app/GetTime" ) else {
            print("Invalid URL")
            return
        }
        let request = URLRequest(url: url)
    
        URLSession.shared.dataTask(with: request) { data, response, error in
            if let data = data {
                print("data: \(data) \(Date())")
                do {
                    let response = try JSONDecoder().decode(ActionClock.self, from: data)
                    print("is this called?")
                    DispatchQueue.main.async {
                        self.atime = response
                        print("in LOADTIME")
                    }
                    print("response: \(response)")
                    return
                    
                }catch {
                    print ("\(error)")
                }
            }
        }.resume()
    }