Error loading from bundle using a file located in the projected

30 views Asked by At

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

There are 0 answers