skip to Main Content

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?

2

Answers


  1. Chosen as BEST ANSWER

    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()
    

    }


  2. Here is how you test for error and json error :

    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
                // always good to test for error
                if let error {
                    print("Error:(error)")
                    print("Error:(error.localizedDescription)")
                    return
                }
                if let data = data {
                    print("data: (data) (Date())")
                    // Do/Catch bloc
                    do {
                        let response = try JSONDecoder().decode(ActionClock.self, from: data)
                        DispatchQueue.main.async {
                            self.atime = response
                            print("in LOADTIME")
                        }
                        print("is this called?")
                        print("response: (response)")
                    } catch {
                        print("error: (error)") // note that for JSON decoding error, using localised description will remove the useful part of the error
                    }
                }
                else{
                    print("I failed")
                }
            }.resume()
        }
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search