NEW: Start my new Ultimate Portfolio App course with a free Hacking with Swift+ trial! >>

SOLVED: The data couldn’t be read because it isn’t in the correct format.

Forums > SwiftUI

this is the API

{
   "ip":"",
   "hostname":"",
   "continent_code":"",
   "continent_name":"",
   "country_code2":"",
   "country_code3":"",
   "country_name":" ",
   "country_capital":"",
   "state_prov":"",
   "district":"",
   "city":"",
   "zipcode":"",
   "latitude":00.00,
   "longitude":00.00,
   "is_eu":"false",
   "calling_code":"+",
   "country_tld":"",
   "languages":"",
   "country_flag":"https",
   "isp":"",
   "connection_type":"",
   "organization":"",
   "asn":"",
   "geoname_id":0000,
   "currency":{
      "name":"",
      "code":"",
      "symbol":""
   },
   "time_zone":{
      "name":"",
      "offset":3,
      "current_time":"",
      "current_time_unix":"",
      "is_dst":"",
      "dst_savings":0
   }
}

and this is structs

struct Location: Codable {
    let ip, hostname, continentCode, continentName: String?
    let countryCode2, countryCode3, countryName, countryCapital: String?
    let stateProv, district, city, zipcode: String?
    let latitude, longitude: Double?
    let isEu, callingCode, countryTLD, languages: String?
    let countryFlag: String?
    let isp, connectionType, organization, asn: String?
    let geonameID: Int?
    let currency: Currency?
    let timeZone: TimeZone?

    enum CodingKeys: String, CodingKey {
        case ip, hostname
        case continentCode
        case continentName
        case countryCode2
        case countryCode3
        case countryName
        case countryCapital
        case stateProv
        case district, city, zipcode, latitude, longitude
        case isEu
        case callingCode
        case countryTLD
        case languages
        case countryFlag
        case isp
        case connectionType
        case organization, asn
        case geonameID
        case currency
        case timeZone
    }
}

struct Currency: Codable {
    let name, code, symbol: String?
}

struct TimeZone: Codable {
    let name: String?
    let offset: Int?
    let currentTime, currentTimeUnix, isDst: String?
    let dstSavings: Int?

    enum CodingKeys: String, CodingKey {
        case name, offset
        case currentTime
        case currentTimeUnix
        case isDst
        case dstSavings
    }
}

but I got this error. (The data couldn’t be read because it isn’t in the correct format.).

I built the structs from a script

   

Is that the actual JSON you are testing with or did you give us an altered version? Because it's not valid JSON due to these lines:

   "latitude":00.00,
   "longitude":00.00,

   "geoname_id":0000,

They should be:

   "latitude":0.0,
   "longitude":0.0,

   "geoname_id":0,

   

yea it is altered, becuase the response contains my information. just I want to know if the structs represent the json structure or not, and how to do it. The actual JSON is valid and contains an actual latitude and longitude.

request function:

    func fetch_location(API_KEY : String, ip : String){
        let API_URL = "https://api.ipgeolocation.io/ipgeo?apiKey=\(API_KEY)&ip=\(ip)"
        guard let url = URL(string: API_URL)else{
            print("invalid url ...")
            return
        }
        let decoder = JSONDecoder()
        let request = URLRequest(url: url)

        URLSession.shared.dataTask(with: request) { (data, response, error) in
            if error != nil{
                print("error 1")
                print(error?.localizedDescription as Any)
            }
            else{
                if let data = data{
                    do{
                    let result = try decoder.decode(Location.self, from: data)
                        print(result )

                    }catch{
                        print("errpr 2")
                        print(error.localizedDescription)
                    }
                }
            }
        }.resume()
    }

   

just I want to know if the structs represent the json structure or not

Yep. Once I corrected the zeroes issue, your structs worked fine to decode the example JSON.

A tip...

When decoding JSON, don't do this:

print(error.localizedDescription)

Do this instead:

print(error)

It will give you much more detail about what the problem is.

This:

The data couldn’t be read because it isn’t in the correct format.

versus this:

dataCorrupted(Swift.DecodingError.Context(codingPath: [], debugDescription: "The given data was not valid JSON.", underlyingError: Optional(Error Domain=NSCocoaErrorDomain Code=3840 "Number with leading zero around character 482."

   

I got the problem when I print(error), I changed latitude, longitude to be String instade of Double, thankyou.

   

I am sorry. but why some properties contains nil inside. The API has a value.

   

Okay, after hunting down some sample API data—which you really should supply with these types of questions, as your structs worked fine with the sample data you did provide but fail with actual sample data from the API documentation—you can make these changes and it should work:

struct Location: Codable {
    let ip, hostname, continentCode, continentName: String?
    let countryCode2, countryCode3, countryName, countryCapital: String?
    let stateProv, district, city, zipcode: String?
    let latitude, longitude: String? //instead of Double?
    let isEu: Bool?  //instead of String?
    let callingCode, countryTld, languages: String? //countryTld instead of countryTLD
    let countryFlag: String?
    let isp, connectionType, organization, asn: String?
    let geonameId: String? //instead of let geonameID: Int?
    let currency: Currency?
    let timeZone: TimeZone?

    enum CodingKeys: String, CodingKey {
        case ip, hostname
        case continentCode
        case continentName
        case countryCode2
        case countryCode3
        case countryName
        case countryCapital
        case stateProv
        case district, city, zipcode, latitude, longitude
        case isEu
        case callingCode
        case countryTld //instead of countryTLD
        case languages
        case countryFlag
        case isp
        case connectionType
        case organization, asn
        case geonameId //instead of geonameID
        case currency
        case timeZone
    }
}

//no changes to Currency struct

struct TimeZone: Codable {
    let name: String?
    let offset: Int?
    let currentTime: String?
    let currentTimeUnix: Date? //instead of String?
    let isDst: Bool? //instead of String?
    let dstSavings: Int?

    enum CodingKeys: String, CodingKey {
        case name, offset
        case currentTime
        case currentTimeUnix
        case isDst
        case dstSavings
    }
}

And then when you create your JSONDecoder, be sure to set its keyDecodingStrategy to .convertFromSnakeCase.

OR, you can add explicit key mappings to your CodingKeys enums, like this:

case continentCode = "continent_code"
case continentName = "continent_name"
//...etc..

Note that doing it this way would allow you to keep countryTLD and geonameID in your Location struct.

case countryTLD = "country_tld"
case geonameID = "geoname_id"

   

Thank you, it was my fault, I just took sampel of resopnses that is not workes in my case, or not represents the actual response from API.

   

Hacking with Swift is sponsored by Instabug

SPONSORED Catch bugs as soon as they happen and know exactly why a crash occurred. Instabug's SDK grabs all the logs they need to fix bugs, crashes and performance issues in minutes instead of days. Get screenshots, device details, network logs, repro steps, and tons of other critical insights needed to resolve issues and prioritize product backlogs straight from your dashboard. It only takes a minute to integrate!

Get started now

Sponsor Hacking with Swift and reach the world's largest Swift community!

Reply to this topic…

You need to create an account or log in to reply.

All interactions here are governed by our code of conduct.

 
Unknown user

You are not logged in

Log in or create account
 

Link copied to your pasteboard.