I'm struggling with a SwiftUI app for iOS17 and SwiftData. I am attempting to download data from a JSON source and store the data in SwiftData. I can download and decode the data into a Swift struct, but have not been able to do so with a SwiftData @Model class. The code below includes both the struct and SwiftData procedures. The SwiftData classes are SDTopLevel and SDFuelStation, the structs are TopLevelX and FuelStationX. The button in FuelStationListView calls either loadDataSD or loadDataX. The URL is correct and contains a demo key.
struct FuelStationListView: View {
@Environment(\.modelContext) var context
@Query(sort: \SDFuelStation.stationName) var fuelStations: [SDFuelStation]
@State private var sdTopLevel: SDTopLevel?
@State private var topLevel: TopLevelX?
var body: some View {
NavigationStack {
Button("Fetch") {
Task {
await loadDataX()//works
//await loadDataSD()//does not work
}
}
List {
ForEach(fuelStations) { fuelStation in
Text(fuelStation.stationName)
}
}
.navigationTitle("Fuel Stations")
}//nav
}//body
func loadDataSD() async {
guard let url = URL(string: "https://developer.nrel.gov/api/alt-fuel-stations/v1.json?api_key=DEMO_KEY&limit=10") else {
print("Invalid URL")
return
}
do {
let (data, response) = try await URLSession.shared.data(from: url)
guard (response as? HTTPURLResponse)?.statusCode == 200 else {
print(response)
return
}
let decoder = JSONDecoder()
decoder.keyDecodingStrategy = .convertFromSnakeCase
let decodedResponse = try decoder.decode(SDTopLevel.self, from: data)
print(decodedResponse)
sdTopLevel = decodedResponse
print("sdTopLevel.fuelStations.count is \(sdTopLevel?.fuelStations.count ?? 1000)")
} catch {
print("Invalid Data")
}
}//load data
func loadDataX() async {
guard let url = URL(string: "https://developer.nrel.gov/api/alt-fuel-stations/v1.json?api_key=DEMO_KEY&limit=10") else {
print("Invalid URL")
return
}
do {
let (data, response) = try await URLSession.shared.data(from: url)
guard (response as? HTTPURLResponse)?.statusCode == 200 else {
print(response)
return
}
let decoder = JSONDecoder()
decoder.keyDecodingStrategy = .convertFromSnakeCase
let decodedResponse = try decoder.decode(TopLevelX.self, from: data)
self.topLevel = decodedResponse
print("topLevel.fuelStations.count is \(topLevel?.fuelStations.count ?? 0)")
for station in decodedResponse.fuelStations {
print(station.stationName)
}
self.topLevel = nil
} catch {
print("Invalid Data")
}
}//load data
}//struct fuel Station list view
And the data:
@Model
class SDFuelStation: Codable {
enum CodingKeys: CodingKey {
case id, city, stationName, streetAddress
}//enum
public var id: Int
var stationName: String = ""
var streetAddress: String = ""
var city: String = ""
public init(stationName: String, streetAddress: String, city: String) {
self.id = 0
self.stationName = stationName
self.streetAddress = streetAddress
self.city = city
}
required public init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
id = try container.decode(Int.self, forKey: .id)
stationName = try container.decode(String.self, forKey: .stationName)
streetAddress = try container.decode(String.self, forKey: .streetAddress)
city = try container.decode(String.self, forKey: .city)
}//required init
public func encode(to encoder: Encoder) throws {
var container = encoder.container(keyedBy: CodingKeys.self)
try container.encode(id, forKey: .id)
try container.encode(stationName, forKey: .stationName)
try container.encode(streetAddress, forKey: .streetAddress)
try container.encode(city, forKey: .city)
}
}//class
struct TopLevelX: Codable {
let fuelStations: [FuelStationX]
}
struct FuelStationX: Codable {
let id: Int
var stationName: String = ""
var streetAddress: String = ""
var city: String = ""
}//struct
The error is in the Model code in a getter:
Any guidance would be appreciated. Xcode 15.0 iOS 17
EDIT: I mistakenly missed the SDTopLevel class:
@Model
class SDTopLevel: Codable {
enum CodingKeys: CodingKey {
case fuelStations
}
var fuelStations: [SDFuelStation]
init(fuelStations: [SDFuelStation]) {
self.fuelStations = fuelStations
}
required public init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
fuelStations = try container.decode([SDFuelStation].self, forKey: .fuelStations)
}
public func encode(to encoder: Encoder) throws {
var container = encoder.container(keyedBy: CodingKeys.self)
try container.encode(fuelStations, forKey: .fuelStations)
}
}
The code for var fuelStations unfolds as the following and the error is on the line: return self.getValue(forKey: .fuelStations) and the error is "Thread 10: EXC_BREAKPOINT (code=1, subcode=0x1a8a5303c)"
{
@storageRestrictions(accesses: _$backingData, initializes: _fuelStations)
init(initialValue) {
_$backingData.setValue(forKey: \.fuelStations, to: initialValue)
_fuelStations = _SwiftDataNoType()
}
get {
_$observationRegistrar.access(self, keyPath: \.fuelStations)
return self.getValue(forKey: \.fuelStations)
}
set {
_$observationRegistrar.withMutation(of: self, keyPath: \.fuelStations) {
self.setValue(forKey: \.fuelStations, to: newValue)
}
}
}

//await loadDataSD()//does not work, is this where you have the error? Is the error about decoding theSDTopLevel, if so can you show the code for yourSDTopLevel.