Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Swift4, JSON, keyNotFound, No value associated with key

I need to do Sunset Sunrise App, and this is my code. But I have this error:

Error serializing json: keyNotFound(Sunrise_Sunset.SunPosition.Results.(CodingKeys in _B4291256871B16D8D013EC8806040532).sunrise, Swift.DecodingError.Context(codingPath: [], debugDescription: "No value associated with key sunrise (\"sunrise\").", underlyingError: nil))

And I don't understand how to fix it. Maybe someone had this problem. I will be grateful for any help) This is the API, which I have: https://sunrise-sunset.org/api

struct SunPosition: Codable {
struct Results: Codable {
    let sunrise: String
    let sunset: String
    let solarNoon: String
    let dayLenght: String
    let civilTwilightBegin: String
    let civilTwilightEnd: String
    let nauticalTwilightBegin: String
    let nauticalTwilightEnd: String
    let astronomicalTwilightBegin: String
    let astronomicalTwilightEnd: String

    enum CodingKey:String, Swift.CodingKey {
        case sunrise = "sunrise"
        case sunset = "sunset"
        case solarNoon = "solar_noon"
        case dayLenght = "day_length"
        case civilTwilightBegin = "civil_twilight_begin"
        case civilTwilightEnd = "civil_twilight_end"
        case nauticalTwilightBegin = "nautical_twilight_begin"
        case nauticalTwilightEnd = "nautical_twilight_end"
        case astronomicalTwilightBegin = "astronomical_twilight_begin"
        case astronomicalTwilightEnd = "astronomical_twilight_end"
    }
  }
}

extension SunPosition.Results {
init(from decoder: Decoder) throws {
    let values = try decoder.container(keyedBy: CodingKeys.self)
    sunrise = try values.decode(String.self, forKey: .sunrise)
    sunset = try values.decode(String.self, forKey: .sunset)
    solarNoon = try values.decode(String.self, forKey: .solarNoon)
    dayLenght = try values.decode(String.self, forKey: .dayLenght)
    civilTwilightBegin = try values.decode(String.self, forKey: .civilTwilightBegin)
    civilTwilightEnd = try values.decode(String.self, forKey: .civilTwilightEnd)
    nauticalTwilightBegin = try values.decode(String.self, forKey: .nauticalTwilightBegin)
    nauticalTwilightEnd = try values.decode(String.self, forKey: .nauticalTwilightEnd)
    astronomicalTwilightBegin = try values.decode(String.self, forKey: .astronomicalTwilightBegin)
    astronomicalTwilightEnd = try values.decode(String.self, forKey: .astronomicalTwilightEnd)
}
}

class ViewController: UIViewController {

override func viewDidLoad() {
    super.viewDidLoad()

    let jsonUrlString = "https://api.sunrise-sunset.org/json?lat=36.7201600&lng=-4.4203400"
    guard let url = URL(string: jsonUrlString) else { return }

    URLSession.shared.dataTask(with: url) { (data, response, error) in

        guard let data = data else { return }

        do {
            let sunPosition = try JSONDecoder().decode(SunPosition.Results.self, from: data)
            print(sunPosition)
        }catch let jsonErr {
            print("Error serializing json:", jsonErr)
        }

    }.resume()
}
}
like image 687
Kirill Grinev Avatar asked Jan 18 '26 05:01

Kirill Grinev


1 Answers

Four issues:

  1. Add let results : Results in the SunPosition struct.
  2. Typo: private enum CodingKeys: String, CodingKey rather than enum CodingKey :String, Swift.CodingKey, note the singular / plural difference, the private attribute is recommended but does not cause the issue.
  3. Wrong type to decode: JSONDecoder().decode(SunPosition.self, from: data) rather than JSONDecoder().decode(SunPosition.Results.self, from: data).
  4. To get the results you have to print(sunPosition.results).

Three notes:

  1. Delete the entire extension. In this case you get the initializer for free.
  2. Add &formatted=0 to the URL and set the dateDecodingStrategy of the decoder to .iso8601 to get Date objects. Change the type of all date related properties from String to Date and the type of dayLenght from String to TimeInterval. To change dateDecodingStrategy write

    let decoder = JSONDecoder()
    decoder.dateDecodingStrategy = .iso8601
    let sunPosition = try decoder.decode(SunPosition.self ...
    
  3. I recommend to handle the status, too. Add this in the SunPosition struct

    let status : String
    
    var success : Bool { return status == "OK" }
    
like image 89
vadian Avatar answered Jan 19 '26 18:01

vadian



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!