SwiftUI — Как я могу получить доступ к данным ответа API с помощью динамического ключа?

#swift #swiftui #decodable

#swift #swiftui #декодируемый

Вопрос:

Заранее спасибо за помощь.
Я впервые работаю над вызовом API в SwiftUI. Я вернул часть данных, используя structs / decodable . И я использую перечисления для доступа к каждому слою. Однако API, который я использую, использует текущую дату в качестве ключа, и я изо всех сил пытаюсь получить доступ к этому слою данных без использования строкового литерала. Есть идеи?
Вот что у меня есть.

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

У меня есть текущая дата, сохраненная в пользовательских настройках по умолчанию. Но если я сделаю что-то вроде

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

Затем я получаю сообщение об ошибке «Необработанное значение для enum case должно быть литералом»

Я не уверен, как еще я могу получить доступ к этому слою, потому что я новичок в SwiftUI.

Вот полная модель данных для справки:

 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"
    }
}
 

Комментарии:

1. Почему бы не использовать словарь, [Строка: НЕОбъект]?

Ответ №1:

Не видя JSON, я просто предполагаю, что вы хотите, чтобы ваша NEObjects структура выглядела так:

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

Это будет декодировать JSON, который выглядит следующим образом:

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

Комментарии:

1. Спасибо! Я не знал, что могу это сделать. Сейчас у меня возникли проблемы с инициализацией значения по умолчанию для состояния. Не могли бы вы объяснить это и для меня? У меня есть это: @State var results = NEOData(elementCount: 0, neObjects: [NEObjects]) но я получаю сообщение об ошибке «Невозможно преобразовать значение типа ‘[NEObjects]. Введите » ожидаемый тип аргумента «NEOBjects»»

Ответ №2:

Конечный результат: у меня был ненужный дополнительный слой. После реализации предложения Джона.

Модель

 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"
    }
}