In my GitHub project, I'm using Codeable with CoreData so I don't need a mediating struct to convert data. I get the data from file, and dump them in CoreData. However, whenever I run my project, the number of items I'm consuming from the original file is different from the total number of items I'm fetching from CoreData. And the number of items fetched are different for every build.
I want to save 1000 items in my Core Data. However, when I build my project, at least 1 out of 5 times, I see that I have fewer than 1000 items. I checked the data I'm getting from my back end, and I'm also checking the data that I'm fetching from Core Data, and the numbers don't match.
On my viewDidLoad, I call retrieveData()
guard let url = URL(string: <URL here>)
else { return }
let task = URLSession.shared.dataTask(
with: url as URL,
completionHandler: { [weak self] (data, response, error) in
guard let dataResponse = data, error == nil else {
print("error!!!")
return
}
self?.decodeAndSaveAndFetch(dataResponse: dataResponse)
}
)
task.resume()
}
This is my decodeAndSaveAndFetch() where I decode the json from my service call, save the items in my CoreData and fetch it to display:
func decodeAndSaveAndFetch(dataResponse: Data) {
let decoder = JSONDecoder()
guard let codingUserInfoKeyContext = CodingUserInfoKey.context else { return }
decoder.userInfo[codingUserInfoKeyContext] = container?.viewContext
container?.performBackgroundTask { [weak self] context in
do {
let filePath = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first
guard let fileName = filePath?.appendingPathComponent("COREDATA.txt") else { return }
try dataResponse.write(to: fileName)
_ = try decoder.decode([Ingredient].self, from: dataResponse)
try context.save()
let request: NSFetchRequest<Ingredient> = Ingredient.fetchRequest()
do {
try print(self?.container?.viewContext.count(for: request))
self?.data = try self?.container?.viewContext.fetch(request)
DispatchQueue.main.async { [weak self] in
self?.tableView.reloadData()
}
} catch {
print("fetch failed")
}
} catch {
print("decode and save failed")
}
}
}
In the COREDATA.txt, I always get 1000 items, but when I print the count for the request, I sometimes get fewer than 1000 items.
Here is my Ingredient class with the fetch method:
extension CodingUserInfoKey {
static let context = CodingUserInfoKey(rawValue: "context")
}
@objc(Ingredient)
public class Ingredient: NSManagedObject, Codable {
public func encode(to encoder: Encoder) throws {
var container = encoder.container(keyedBy: CodingKeys.self)
do {
try container.encode(id ?? "blank", forKey: .id)
} catch {
print("error")
}
}
required convenience public init(from decoder: Decoder) throws {
// return the context from the decoder userinfo dictionary
guard let contextUserInfoKey = CodingUserInfoKey.context,
let managedObjectContext = decoder.userInfo[contextUserInfoKey] as? NSManagedObjectContext,
let entity = NSEntityDescription.entity(forEntityName: "Ingredient", in: managedObjectContext)
else {
fatalError("decode failure")
}
// Super init of the NSManagedObject
self.init(entity: entity, insertInto: managedObjectContext)
let values = try decoder.container(keyedBy: CodingKeys.self)
do {
id = try values.decode(String.self, forKey: .id)
desc = try values.decode(String.self, forKey: .desc)
ingredientType = try values.decode(Set<IngredientType>.self, forKey: .ingredientType)
} catch {
print ("error")
}
}
enum CodingKeys: String, CodingKey {
case id = "id"
case desc = "desc"
case ingredientType = "ingredientType"
}
}
extension Ingredient {
@nonobjc public class func fetchRequest() -> NSFetchRequest<Ingredient> {
return NSFetchRequest<Ingredient>(entityName: "Ingredient")
}
@NSManaged public var id: String?
@NSManaged public var desc: String?
@NSManaged public var ingredientType: Set<IngredientType>?
}
Here is the relationship between Ingredient and IngredientType:
Please advise. Thank you!
EDIT: Link to GitHub project To reproduce: build and run in XCode - observe the counts.