I am getting the following error:
Error loading from bundle: keyNotFound(CodingKeys(stringValue: "id", intValue: nil), Swift.DecodingError.Context(codingPath: [], debugDescription: "No value associated with key CodingKeys(stringValue: "id", intValue: nil) ("id").", underlyingError: nil))
I added an id into my model struct so that I could use it with the List function. What else could I do to load the data from the file in the xcode project named apilist.json? The data was loading before I added the id.
"count": 1425,
"entries": [
{
"API": "AdoptAPet",
"Description": "Resource to help get pets adopted",
"Auth": "apiKey",
"HTTPS": true,
"Cors": "yes",
"Link": "https://www.adoptapet.com/public/apis/pet_list.html",
"Category": "Animals"
},
{
"API": "Axolotl",
"Description": "Collection of axolotl pictures and facts",
"Auth": "",
"HTTPS": true,
"Cors": "no",
"Link": "https://theaxolotlapi.netlify.app/",
"Category": "Animals"
},
<elements of entry repeat>
}
]
}
Here is my model:
struct ApiData: Codable, Identifiable {
var id = UUID()
let count: Int
let entries: [ApiEntry]
}
struct ApiEntry: Codable, Hashable {
let api, description, auth: String
let https: Bool
let cors: String
let link: String
let category: String
enum CodingKeys: String, CodingKey {
case api = "API"
case description = "Description"
case auth = "Auth"
case https = "HTTPS"
case cors = "Cors"
case link = "Link"
case category = "Category"
}
}
Here is my list view:
import SwiftUI
struct ApiListView: View {
@State private var apiData: ApiData?
@State private var showError = false
@State private var selectedApi: ApiEntry?
let apiStore = ApiStore()
var body: some View {
NavigationStack {
let entries = apiData?.entries ?? []
List(entries, id: \.self) { entry in
Text(entry.api)
.onTapGesture {
self.selectedApi = entry
}
}
.navigationTitle("API List")
.navigationDestination(for: ApiEntry.self) { entry in // And this line
ApiDetailsView(apiEntry: entry)
}
}
.onAppear {
apiData = apiStore.loadApiData()
showError = (apiData == nil)
}
.alert("Data Not Found", isPresented: $showError) {
Button("OK", role: .cancel) { }
}
}
}
Here is my store:
import Foundation
class ApiStore {
private let fileManager = FileManager.default
private let documentsUrl: URL // Get the URL for the user's documents directory
private let dataFileName = "apilist.json"
init() {
guard let documentsUrl = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first else {
fatalError("Unable to locate Documents directory")
}
self.documentsUrl = documentsUrl
}
func loadApiData() -> ApiData? {
// 1. Attempt to load from App Bundle
if let bundleUrl = Bundle.main.url(forResource: dataFileName, withExtension: nil) {
do {
return try loadFrom(url: bundleUrl)
} catch {
print("Error loading from bundle: \(error)")
}
}
// 2. Attempt to load from Documents directory
let documentsPath = documentsUrl.appendingPathComponent(dataFileName)
do {
print("Trying to load from Documents: \(documentsPath)")
// let data = try Data(contentsOf: documentsPath)
// let decoder = JSONDecoder()
return try loadFrom(url: documentsPath)
} catch {
print("Error loading from documents: \(error)")
return nil // Data not found
}
}
private func loadFrom(url: URL) throws -> ApiData {
let data = try Data(contentsOf: url)
let decoder = JSONDecoder()
return try decoder.decode(ApiData.self, from: data)
}
func saveApiData(_ apiData: ApiData) {
let documentsPath = documentsUrl.appendingPathComponent(dataFileName)
let encoder = JSONEncoder()
do {
let data = try encoder.encode(apiData)
try data.write(to: documentsPath)
} catch {
print("Error saving data: \(error)")
}
}
}