Updating existing property in CoreData fails - ios

I have a Job Model in CoreData and within this object I have values like job_id and start_date and a one-to-many relation to JobImage which is also a CoreData Model.
If I create a new Job object and save it together with some Images, everything works well. This looks like that:
let appDelegate = UIApplication.sharedApplication().delegate as! AppDelegate
let managedContext = appDelegate.managedObjectContext
let entity = NSEntityDescription.entityForName("Job", inManagedObjectContext: managedContext)
job = Job(entity: entity!, insertIntoManagedObjectContext: managedContext)
job?.setValue(NSDate(), forKey: "start_date")
job?.setValue(true, forKey: "running")
if let id = jobJson.id {
job?.setValue(id, forKey: "job_id")
}
do {
try managedContext.save()
} catch let error as NSError {
print("Could not save \(error), \(error.userInfo)")
} catch {
print("Something unexpected happened...")
}
and adding images like that:
let appDelegate = UIApplication.sharedApplication().delegate as! AppDelegate
let managedContext = appDelegate.managedObjectContext
let jpegImage = UIImageJPEGRepresentation(image, 1.0)
let entity = NSEntityDescription.entityForName("JobImage", inManagedObjectContext: managedContext)
let imageObject = JobImage(entity: entity!, insertIntoManagedObjectContext: managedContext)
imageObject.data = jpegImage
imageObject.job = job
do {
try managedContext.save()
} catch let error as NSError {
print("Could not save \(error), \(error.userInfo)")
} catch {
print("Something unexpected happened...")
}
where job is the Job object which I created earlier (I saved it in the global variable like that job = Job(entity: entity!, insertIntoManagedObjectContext: managedContext)) and image is just a UIImage.
The JobImage object has a data property which is "Binary Data".
I set the job property which is the relation to my Job object to job and save the result.
Everything I mentioned until now works great but if I don't create a new entity but I fetch an existing one and try to add a relation to a new JobImage and save the result, it fails. Fetching the object works great, I can access all the properties stored within the Job object but saving another image to the images relation fails.
I fetch the Job object like that:
let appDelegate = UIApplication.sharedApplication().delegate as! AppDelegate
let managedContext = appDelegate.managedObjectContext
let fetchRequest = NSFetchRequest(entityName: "Job")
fetchRequest.predicate = NSPredicate(format: "running == true")
do {
let results = try managedContext.executeFetchRequest(fetchRequest)
let jobs = results as! [Job]
if jobs.count > 0 {
job = jobs[0]
}
} catch let error as NSError {
print("Could not fetch \(error), \(error.userInfo)")
} catch {
print("Something unexpected happened...")
}
Again, I store the Job object in my global property job and use it to create new relations to images and save them as before but this time it fails with the following error:
Could not save Error Domain=NSCocoaErrorDomain Code=1550 "The operation couldn’t be completed. (Cocoa error 1550.)" UserInfo={NSValidationErrorObject= (entity: Job; id: 0xd000000000040000
...
NSValidationErrorValue: Relationship 'job_images' on managed object (0x7fb094029f10) (entity: Job; id: 0xd000000000040000
...
NSValidationErrorKey: job_images, NSLocalizedDescription: The operation couldn’t be completed. (Cocoa error 1550.)]
Where job_images is my relation from Job to JobImage.
I read through some StackOverflow questions with Cocoa error 1550 but none of the answers seem to work for my problem. Do you have any ideas?

Related

Core Data operations on multiple thread causing insertion but no fetching

I am using CoreData to insert data and fetch Data, as I have a lot of data, so I am using core data on multiple threads in order to be thread-safe.
The problem is that I am able to insert Data in CoreData, but while fetching from CoreData, the results are zero, this is happening when I kill my app and fetch the data from Database. This has something to do with NSMangedObjectContext but I am not able to figure it out.
Here is my code snippet :
class CoreDataManager {
static let sharedManager = CoreDataManager()
private init() {}
lazy var persistentContainer: NSPersistentContainer = {
let container = NSPersistentContainer(name: "My_Contacts")
container.loadPersistentStores(completionHandler: { (storeDescription, error) in
if let error = error as NSError? {
fatalError("Unresolved error \(error), \(error.userInfo)")
}
})
return container
}()
func saveContext() {
let context = CoreDataManager.sharedManager.persistentContainer.viewContext
if context.hasChanges {
do {
try context.save()
} catch {
let nserror = error as NSError
fatalError("Unresolved error \(nserror), \(nserror.userInfo)")
}
}
}
func insertContact(id:Int, firstName : String,lastName : String,emaild : String,isFavorite : Bool,phoneNum : String,profilePic : String,sync : Bool) -> Contact? {
let managedContext = CoreDataManager.sharedManager.persistentContainer.viewContext
let privateManagedObjectContext: NSManagedObjectContext = {
//NSPrivateQueueConcurrencyType
let moc = NSManagedObjectContext(concurrencyType: .privateQueueConcurrencyType)
moc.parent = managedContext
return moc
}()
let entity = NSEntityDescription.entity(forEntityName: "Contact",
in: privateManagedObjectContext)!
let contact = NSManagedObject(entity: entity,
insertInto: privateManagedObjectContext)
contact.setValue(firstName, forKey: "first_name")
contact.setValue(lastName, forKey: "last_name")
contact.setValue(emaild, forKey: "email")
contact.setValue(isFavorite, forKey: "favorite")
contact.setValue(phoneNum, forKey: "phone_number")
contact.setValue(profilePic, forKey: "profile_pic")
contact.setValue(sync, forKey: "syncStatus")
contact.setValue(id, forKey: "contactId")
do {
try privateManagedObjectContext.save()
return contact as? Contact
} catch let error as NSError {
print("Could not save. \(error), \(error.userInfo)")
return nil
}
}
func fetchAllContacts() -> [Contact]?{
let managedContext = CoreDataManager.sharedManager.persistentContainer.viewContext
let fetchRequest = NSFetchRequest<NSManagedObject>(entityName: "Contact")
do {
let people = try managedContext.fetch(fetchRequest)
return people as? [Contact]
} catch let error as NSError {
print("Could not fetch. \(error), \(error.userInfo)")
return nil
}
}
}
so I am using core data on multiple threads in order to be thread-safe
What do you mean by this? Using multiple threads doesn't make anything thread-safe. Thread safety relates to your ability to run your code on multiple threads without problems, and it generally requires that you take a number of precautions to prevent threads from interfering with each other.
The problem is that I am able to insert Data in CoreData, but while fetching from CoreData, the results are zero, this is happening when I kill my app and fetch the data from Database. This has something to do with NSMangedObjectContext but I am not able to figure it out.
You need to understand what a managed object context is. Think of it like a temporary workspace: you can execute a fetch request to bring objects from a persistent store into a managed object context, and you can add new objects to the context, and you can manipulate the objects in a context. The changes you make in a context don't mean anything outside the context until you save the context back to the persistent store.
A few reasons you might not be seeing the objects you're adding are:
You're adding the objects and trying to read them back in different contexts.
You never save the context after you add objects.
You save the context in which you added the object, but the parent context (managed object contexts are hierarchical) is never saved.
You try to save the context after you add objects, but saving fails.
You're using the same context in multiple threads without taking care to serialize the operations on the context (which is to say, your code isn't thread-safe).
What you really should do to figure this out is to get yourself back to a state where you can store and retrieve objects reliably. Try using just one thread and make sure that your operations work. If they don't, fix that first. Next, get a solid understanding of how managed object contexts work and how to use them. Finally, read up on concurrency and Core Data.
Since you are using multiple MOC(Managed Object Context), you need to save both the contexts
You have set privateManagedObjectContext's parent to managedContext, but you are not saving managedContext
After calling privateManagedObjectContext.save(), you need to call managedContext.save() as well

Core Data Merge policy

I've implemented core data in my application successfully. Everything was working fine but I got an issue. I'm using merge policy to update records.
I've two Entities name Issues and Members with relation one to many. One issue has many members.
Data comes from the server and saved in these two Entities:
Object A
Object B
Object C
This data comes first time and save in coredata. Data updates on server and when fetching second time , this data comes:
Object A
Object B
It should update and remove the Object C, but Object C still in coredata.
Please help me what i'm doing wrong here. Thanks in advance.
This is my code:
private class func getContext() -> NSManagedObjectContext {
let appDelegate = UIApplication.shared.delegate as! AppDelegate
return appDelegate.persistentContainer.viewContext
}
class func saveIssues(with json: JSON)
{
let context = getContext()
let entity = NSEntityDescription.entity(forEntityName: "Issues", in: context)
let issue = NSManagedObject(entity: entity!, insertInto: context) as! Issues
issue.setValue(json["Id"].stringValue, forKey: "id")
issue.setValue(json["Name"].stringValue, forKey: "name")
issue.setValue(json["CreatedByName"].stringValue, forKey:"createdByName")
for issueMembers in json["Members"].arrayValue
{
let members = NSEntityDescription.insertNewObject(forEntityName: "Members", into: context) as! Members
members.setValue(issueMembers["FullName"].stringValue, forKey: "fullName")
members.setValue(issueMembers["PictureUrl"].stringValue, forKey: "picture_Url")
members.setValue(issueMembers["LoginId"]. stringValue, forKey: "loginId")
issue.addToIssueMembers(members)
}
context.mergePolicy = NSMergeByPropertyObjectTrumpMergePolicy
do{
try context.save()
}catch let error as NSError {
print("Could not save \(error), \(error.userInfo)")
}
}
I investigated it a little bit and found that no one merge policy suites your needs: in case of uniqueness constraints, both NSRollbackMergePolicy and NSOverwriteMergePolicy save only old (existed on persistent store) set of objects in relationship, and both NSMergeByPropertyStoreTrumpMergePolicy and NSMergeByPropertyObjectTrumpMergePolicy save all objects in relationship the object had in two versions (on disk and in memory). So seems like you have to manage the relationship yourself...

Getting nil value from entity in core data

I am following raywenderlich's tutorial for Core Data : Tutorial
the issue I am facing is that in this snippet of code :
let entity = NSEntityDescription.entity(forEntityName: "Person", in: managedContext)!
let person = NSManagedObject(entity: entity,insertInto: managedContext)
The entity value is returned nil and app gets crashed . Is there something that I missed because I have tried 3-4 times but still the same issue
If you say what you say is correct (crashing on the mentioned line) then the Person entity does not exist in the data model.
Please verify that the person exists in the model:
Also following the linked tutorial I suspect you're calling the save method, like so:
func save(name: String) {
guard let appDelegate =
UIApplication.shared.delegate as? AppDelegate else {
return
}
// 1
let managedContext =
appDelegate.persistentContainer.viewContext
// 2
let entity =
NSEntityDescription.entity(forEntityName: "Person",
in: managedContext)!
let person = NSManagedObject(entity: entity,
insertInto: managedContext)
// 3
person.setValue(name, forKeyPath: "name")
// 4
do {
try managedContext.save()
people.append(person)
} catch let error as NSError {
print("Could not save. \(error), \(error.userInfo)")
}
}
Which suggests that you'll also have to check for the name attribute on the Person entity:
I hope this helps you.

Issue with updating a record in CoreData

I did go through other posts that dealt this issue. But I couldn't find much with regard to my problem. Hope somebody can help. My issue is...I am having a certain edited record that I want to display in my tableview. For that I want to update that entry in Core-Data also. I am not able to figure out how that can be done.
This is how I am bringing the edited data in tableview and saving in Core Data. The updation has to be done somewhere in between but I am not able to figure out exactly how and where..?
#IBAction func saveToMainEditViewController (segue:UIStoryboardSegue) {
let detailViewController = segue.source as! EditCategoriesTableViewController
let index = detailViewController.index
let modelString = detailViewController.editedModel //Edited model has the edited string
let myCategory1 = Category(context: self.context)
myCategory1.categoryName = modelString
mangObjArr[index!] = myCategory1
//Saving to CoreData
guard let appDelegate = UIApplication.shared.delegate as? AppDelegate else {
return
}
let managedContext = appDelegate.persistentContainer.viewContext
let entity = NSEntityDescription.entity(forEntityName: "Category", in: managedContext)
let category = NSManagedObject(entity: entity!, insertInto: managedContext)
category.setValue(myCategory1.categoryName, forKeyPath: "categoryName")
category.setValue(myCategory1.categoryId, forKey: "categoryId")
do {
try managedContext.save()
} catch let error as NSError {
print("Could not save. \(error), \(error.userInfo)")
}
}
Steps:
Understand basic concepts
Fetch Record
Update Record
Save context
Concepts:
This is just a crude explanation, the proper explanation is in the link below.
Though it is time consuming, please refer to the link below, it will help you understand CoreData. If you don't understand you will encounter a lot of problems later on.
Entity:
In the core data model you can create entities, these are the tables.
Managed Object:
This is the class representation of the entity
Each instance of this class would represent a single row in the table.
Managed Object Context:
Imagine managed object context like a piece of paper / scratch pad
The managed objects are created / updated / deleted on a specific managed object context.
You can save / discard the changes made to a managed object context.
Not Thread Safe:
When ever you perform anything on a managed object context, make sure you use within context.performAndWait { }. This will ensure that context operations are performed on the context's queue (thread).
Fetch and Update:
func fetch() {
let request : NSFetchRequest< Category> = Category.fetchRequest()
//Predicate builds the where clause to filter records
//This is a sample, so edit based on your requirement
request.predicate = NSPredicate(format: "categoryID = %#", argumentArray: [10])
context.performAndWait {
do {
let categories = try context.fetch(request)
//Update
for category in categories {
category.name = "aaa"
}
}
catch {
print("error = \(error)")
}
}
}
Save:
func save() {
if context.hasChanges {
context.performAndWait {
do {
context.save()
}
catch {
print("Save error: \(error)")
}
}
}
}
Reference:
https://developer.apple.com/library/content/documentation/Cocoa/Conceptual/CoreData/index.html

only saving last item while saving in core data swift [duplicate]

This question already has answers here:
Core Data in Swift: Only saving last object in a for loop
(4 answers)
Closed 6 years ago.
With this function I want to add attributes in core-data entity but it saves only last item repeatedly.
what am I doing wrong?
func SetFeaturedValues(Array : NSArray){
let appDelegate =
UIApplication.sharedApplication().delegate as! AppDelegate
let managedContext = appDelegate.managedObjectContext
let entity = NSEntityDescription.entityForName("FeaturedJob",
inManagedObjectContext:managedContext)
let FeaturedJob = NSManagedObject(entity: entity!,
insertIntoManagedObjectContext: managedContext)
print("array is \(Array)")
for i in Array{
if let rowData :NSDictionary = i as? NSDictionary{
FeaturedJob.setValue(rowData["company"], forKey: "company")
FeaturedJob.setValue(rowData["city"], forKey: "city")
FeaturedJob.setValue(rowData["id"], forKey: "id")
FeaturedJob.setValue(rowData["user_logo"], forKey: "user_logo")
FeaturedJob.setValue(rowData["title"], forKey: "title")
do {
try managedContext.save()
self.Featured.append(FeaturedJob)
print("featured job is \(FeaturedJob)")
// people.append(person)
} catch let error as NSError {
print("Could not save \(error), \(error.userInfo)")
}
}
}
}
Ok, so this could still do with some tidying up but...
What's going wrong is that you are creating only one singleNSManagedObject instance called FeaturedJob. Every time you iterate through the array you append its value and save it. But its the same instance! That's why only one instance gets saved.
So put the line with let let FeaturedJob = ... inside the if block just before you start assigning values. That way you're going to get a new one each time.
PS: You are saving the context with every iteration. You probably only want to do that once at the end of the function.

Resources