简体   繁体   中英

SwiftUI - How can I access API response data with a dynamic key?

Thanks for the help in advance.
I'm working on an API call in SwiftUI for the first time. I've got a portion of the data back by using structs/decodable. And I use enums to access each layer. However, the API that I'm using uses the current date as a key, and I'm struggling to access that layer of data without using a string literal. Any Ideas?
Here's what I have.

struct NEObjects: Decodable {
    let fromToday: [NEObject]
    
    enum CodingKeys: String, CodingKey {
        case  fromToday = "2021-02-16"
    }
}

I have the current date stored in the user defaults. But if I do something like

let date = UserDefaults.standard.string(forKey: "current_date")
case fromToday = date

Then I get an error saying "Raw value for enum case must be a literal"
Im' not sure how else I can access this layer because I'm new to swiftUI.
Here is the full data model for reference:

import Foundation

let date = UserDefaults.standard.string(forKey: "current_date")

struct NEOData: Decodable  {
    let elementCount: Int
    let neObjects: NEObjects
    
    enum CodingKeys: String, CodingKey {
        case elementCount = "element_count"
        case neObjects = "near_earth_objects"
    }
}

struct NEObjects: Decodable {
    let fromToday: [NEObject]
    
    enum CodingKeys: String, CodingKey {
        case  fromToday = "2021-02-15"
    }
}

struct NEObject: Decodable {
    let name: String
    let absoluteMagnitude: Double
    let diameter: Diameter
    let isHazardous: Bool
    let closeApproachData: [CloseApproachData]
    let isSentry: Bool
    
    enum CodingKeys: String, CodingKey {
        case name = "name"
        case absoluteMagnitude = "absolute_magnitude_h"
        case diameter = "estimated_diameter"
        case isHazardous = "is_potentially_hazardous_asteroid"
        case closeApproachData = "close_approach_data"
        case isSentry = "is_sentry_object"
    }
}

struct Diameter: Decodable {
    let kilometers: Kilometers
    let meters: Meters
    let miles: Miles
    let feet: Feet
}

struct CloseApproachData: Decodable {
    let velocity: Velocity
    let missDistance: MissDistance
    let date: String
    
    enum CodingKeys: String, CodingKey {
        case velocity = "relative_velocity"
        case missDistance = "miss_distance"
        case date = "close_approach_date"
    }
}

struct Velocity: Decodable {
    let mph: String
    let kph: String
    
    enum CodingKeys: String, CodingKey {
        case mph = "miles_per_hour"
        case kph = "kilometers_per_hour"
    }
}

struct MissDistance: Decodable {
    let kilometers: String
    let miles: String
}

struct Kilometers: Decodable {
    let min: Double
    let max: Double
    
    enum CodingKeys: String, CodingKey {
        case min = "estimated_diameter_min"
        case max = "estimated_diameter_max"
    }
}

struct Meters: Decodable {
    let min: Double
    let max: Double
    
    enum CodingKeys: String, CodingKey {
        case min = "estimated_diameter_min"
        case max = "estimated_diameter_max"
    }
}

struct Miles: Decodable {
    let min: Double
    let max: Double
    
    enum CodingKeys: String, CodingKey {
        case min = "estimated_diameter_min"
        case max = "estimated_diameter_max"
    }
}

struct Feet: Decodable {
    let min: Double
    let max: Double
    
    enum CodingKeys: String, CodingKey {
        case min = "estimated_diameter_min"
        case max = "estimated_diameter_max"
    }
}

Without seeing the JSON, I'm just guessing you want your NEObjects struct to look like this:

struct NEObjects: Decodable {
    let objectsByDay : [String : NEObject]    
}

This would decode JSON that looks like this:

{
   "2021-02-16" : [
       { ... a bunch of your custom objects ... }
   ]
}

Final result: I had an unnecessary extra layer. After implementing the suggestion by John.

Model

import Foundation

struct NEOData: Decodable  {
    let elementCount: Int
    let neObjects: [String : [NEObject]]
    
    enum CodingKeys: String, CodingKey {
        case elementCount = "element_count"
        case neObjects = "near_earth_objects"
    }
}

struct NEObject: Decodable {
    let name: String
    let absoluteMagnitude: Double
    let diameter: Diameter
    let isHazardous: Bool
    let closeApproachData: [CloseApproachData]
    let isSentry: Bool
    
    enum CodingKeys: String, CodingKey {
        case name = "name"
        case absoluteMagnitude = "absolute_magnitude_h"
        case diameter = "estimated_diameter"
        case isHazardous = "is_potentially_hazardous_asteroid"
        case closeApproachData = "close_approach_data"
        case isSentry = "is_sentry_object"
    }
}

struct Diameter: Decodable {
    let kilometers: Kilometers
    let meters: Meters
    let miles: Miles
    let feet: Feet
}

struct CloseApproachData: Decodable {
    let velocity: Velocity
    let missDistance: MissDistance
    let date: String
    
    enum CodingKeys: String, CodingKey {
        case velocity = "relative_velocity"
        case missDistance = "miss_distance"
        case date = "close_approach_date"
    }
}

struct Velocity: Decodable {
    let mph: String
    let kph: String
    
    enum CodingKeys: String, CodingKey {
        case mph = "miles_per_hour"
        case kph = "kilometers_per_hour"
    }
}

struct MissDistance: Decodable {
    let kilometers: String
    let miles: String
}

struct Kilometers: Decodable {
    let min: Double
    let max: Double
    
    enum CodingKeys: String, CodingKey {
        case min = "estimated_diameter_min"
        case max = "estimated_diameter_max"
    }
}

struct Meters: Decodable {
    let min: Double
    let max: Double
    
    enum CodingKeys: String, CodingKey {
        case min = "estimated_diameter_min"
        case max = "estimated_diameter_max"
    }
}

struct Miles: Decodable {
    let min: Double
    let max: Double
    
    enum CodingKeys: String, CodingKey {
        case min = "estimated_diameter_min"
        case max = "estimated_diameter_max"
    }
}

struct Feet: Decodable {
    let min: Double
    let max: Double
    
    enum CodingKeys: String, CodingKey {
        case min = "estimated_diameter_min"
        case max = "estimated_diameter_max"
    }
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM