1

My app has a Core Data entity (called Product) that has two attributes:

  • A name of type String
  • An image of type Binary Data (with "Allows External Storage" checked)

Right now since I am developing, I create a default object in my initial viewDidLoad with hardcoded values for name and image. After the object is created, I can pass it around and use it in my app without issue.

However, if I update the name attribute to a new string value, and re-run the app, the image attribute becomes nil (this behavior only happens after I re-run the app, or terminate and re-open it). Do you know what might be causing this behavior?

This is how I change the name value:

DataController.shared.viewContext.perform {

    if mike {
        product.name = "Mike"
    } else {
        product.name = "Bob"
    }
    try? DataController.shared.viewContext.save()
}

I am using a DataController class to interface with the persistent store:

   class DataController {

    static let shared = DataController(modelName: "my_model")        
    let persistentContainer: NSPersistentContainer
    var viewContext: NSManagedObjectContext {
        return persistentContainer.viewContext
    }
    private init(modelName: String) {
        persistentContainer = NSPersistentContainer(name: modelName)
    }
    func configureContexts() {
        viewContext.automaticallyMergesChangesFromParent = true
        viewContext.mergePolicy = NSMergePolicy.mergeByPropertyStoreTrump
   }
}

I'm also using NSFetchedResultsController. This is how I set it up in my viewDidLoad:

fileprivate func setupFetchedResultsController() {
    let fetchRequest:NSFetchRequest<Product> = Product.fetchRequest()
    let sortDescriptor = NSSortDescriptor(key: "salesByVolume", ascending: false)
    fetchRequest.sortDescriptors = [sortDescriptor]
    fetchedResultsController = NSFetchedResultsController(fetchRequest: fetchRequest, managedObjectContext: DataController.shared.viewContext, sectionNameKeyPath: nil, cacheName: "products")
    fetchedResultsController.delegate = self
    do {
        try fetchedResultsController.performFetch()
    } catch {
        fatalError("The request could not be performed: \(error.localizedDescription)")
    }
}

Thank you!

blrsk
  • 185
  • 1
  • 4
  • 11
  • 2
    This questions already exists in more detail here: https://stackoverflow.com/questions/52586564/ios-12-specific-problem-core-data-external-storage-binary-data-corruption – zendo Oct 02 '18 at 10:30

1 Answers1

1

You might wanna try the following:

DataController.shared.viewContext.perform {
    product.setValue("Mike", forKey: "name")
    try? DataController.shared.viewContext.save()
}

You need to set value for a key to update it's value, then you can save it. I hope this helps

emrepun
  • 2,496
  • 2
  • 15
  • 33
  • 1
    Thanks for the suggestion! I implemented that an also removed an if/else conditional I had put inside the DataController.shared.viewContext.perform. The behavior seems to be gone now... – blrsk Oct 01 '18 at 21:16
  • should I always use setValue(), even when I create a new object? I've been using "product.key = value" when I create the object. – blrsk Oct 01 '18 at 21:28
  • Well Im not sure, but when I create a new object, I also do like you. But when I need to update a value of an object, I do it with setValue() :) – emrepun Oct 01 '18 at 21:34
  • I am now using setValue() everywhere and things seem to work as expected... – blrsk Oct 01 '18 at 21:35
  • Sounds like its better to use that. – emrepun Oct 01 '18 at 21:36
  • Why did you pull your best answer back :/ Also someone else gave me a minus point I dont know why, and now I feel like deleting my answer even though it solves the problem – emrepun Oct 01 '18 at 22:38
  • Hey @emrepun, sorry for the confusion. I gave you a point but it turns out this does not solve the problem so I removed the checkmark :-/ Thanks a lot for your help though ! Do you think there is anything else I could try to fix this issue? – blrsk Oct 02 '18 at 02:08