Iphone: Error while Inserting Objects in Core Data - iphone

I have a Uitableview that loads data from a parsed xml feed. when the feed is first parsed all the text data is stored in an entity NewsItems in core data. after the table is loaded the images asociated with each object are fetched asynchronously and stored in a separate entity NewsImages, after the feeds/images are stored locally all data is fetched locally next time the app starts. NewsItems and NewsImages have a one to one relationship with each other.
I have a refresh button which when clicked deletes all entries in NewsItems, this will also delete all objects in NewsImages associated with objects in NewsItems aswell, since the relationship delete rules are cascade. After deleteion, the feed is parsed again and data is stored locally again.
My problem is when I do this multiple number of times quickly. I get this error while saving Images locally.
*** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: 'Unacceptable type of value for to-one relationship: property = "ItemImage"; desired type = NewsImages; given type = NewsImages; value = <NewsImages: 0x68c49f0> (entity: NewsImages; id: 0x6804730 <x-coredata:///NewsImages/t5444BEE7-6193-4C25-8AAB-F64113BEAB7546> ; data: {
Image = <ffd8ffe0 00104a46 49460001 01000001 00010000 ffe10058 45786966 00004d4d 002a0000 00080002 01120003 00000001 0001>;
ImageItem = nil;
}).'
This is the function responsible for inserting images
-(void)setImage:(UIImage*)moImage ForObject:(NSManagedObjectID*)moID{
NewsItems *newsItem = (NewsItems*)[self.managedObjectContext objectWithID:moID];
NewsImages *newsImage = (NewsImages*)[NSEntityDescription insertNewObjectForEntityForName:#"NewsImages" inManagedObjectContext:self.managedObjectContext];
newsImage.Image = UIImageJPEGRepresentation(moImage,1.0);
newsItem.ItemImage = newsImage;
[self commitSave];
}

I think the most likely problem is that you are passing the objectID for a NewsImages object instead of a for NewsItems object in setImage:ForObject: and in this line:
NewsItems *newsItem = (NewsItems*)[self.managedObjectContext objectWithID:moID];
...you are actually getting a NewsImages object returned cast as a NewsItems.
Cast can be problematical in Objective-C because an object will not complain at return if you send it a message it does not understand. The compiler won't catch the error because of the cast.
I would remove the cast, set the return item to id like so:
id newsItem = (NewsItems*)[self.managedObjectContext objectWithID:moID];
... then log its class:
NSLog(#"class=%#",[newItem class]);
... or check it in the debugger. Since you seem to be using custom NSManagedObject subclasses this should confirm the source of the error.

I had the same problem.
I fixed it by providing a class in the entity editor for the derived class.
The following setup was producing the error you've described:
Entity name : Contact <-------- User
ObjC Class : Contact Default to NSManagedObject.
The following setup fixed it:
Entity name : Contact <-------- User
ObjC Class : Contact Contact

Related

Realm object property misses its value, but I can see it when printing the whole object

I stumbled upon a weird thing when trying to fetch an object from my Realm (iOS, Swift, Realm version 0.98.2)
print("speaker:")
print(RealmProvider.appRealm.objects(FavoriteSpeaker).first!)
Correctly dumps my object in the console:
speaker:
FavoriteSpeaker {
name = Ashley Nelson-Hornstein;
}
But when I try to get the name property's value:
print("speaker name:")
print(RealmProvider.appRealm.objects(FavoriteSpeaker).first!.name)
I get an empty string 🤔
speaker name:
The four lines are together in my model's init method
Update 1: I found an answer that suggests that you merely don't see the values when printed in the Console: Realm object is missing all properties except primaryKey but I also tried displaying the name property via an alert view and that is also empty.
Update 2: Just to make sure that everything happens sequentially and on the same thread I did this:
let favorite1 = FavoriteSpeaker()
favorite1.name = "Debbie Downer"
try! RealmProvider.appRealm.write {
RealmProvider.appRealm.deleteAll()
RealmProvider.appRealm.add(favorite1)
}
print("speaker:")
print(RealmProvider.appRealm.objects(FavoriteSpeaker.self).first!)
print("speaker name:")
print(RealmProvider.appRealm.objects(FavoriteSpeaker.self).first!.name)
But the result is the same - printing name prints an empty string
The name property is probably not declared as dynamic, which leads to it reading the nil value stored on the object itself rather than reading the data from the Realm.

Parse local datastore relation

I've a class called category, and I have retrieved my categories from local datastore through query.getFromLocalDataStore().
Next I've the following code
var userExpense = PFObject(className: "Expenses")
userExpense["category"] = category
userExpense.saveEventually()
userExpense.pin()
And now i'm getting this error,
{error={"__type":"Pointer","className":"Category","localId":"local_55876b12913120b9"} is not a valid Pointer, code=106}
Any idea why? I wanted
If I'm correct, you downloaded the category variable as a PFObject, not as a pointer. What you can do, is to create a pointer from your category with the method:
objectWithoutDataWithClassName:objectId:
and set that to userExpense["category"], and that will be a valid pointer.

iPhone core data can I cache NSManagedObjects?

I'm running a data model in one of my apps, where an event has an "eventType" relationship defined. This allows me to modify the look and feel of multiple events by changing their "eventType" relationship object.
The problem that I'm running into is that before I insert an event, I check if a typeRelationship for this object is present with the code below. This takes some time if I need to insert a large number of objects.
Can I cache the results of this fetch request (for example in NSMutableDictionary) and check that dictionary (local memory) to see if there is an NSManagedObject with the given EventIDEnum? Can I keep the cache alive forever, or will the underlying objects get "out of date" after a while?
-(Event*)insertAndReturnNewObjectWithTypeID:(EventIDEnum)eventTypeID date:(NSDate*)date
{
NSFetchRequest *eventTypesArray = [NSFetchRequest fetchRequestWithEntityName:#"EventType"];
eventTypesArray.predicate = [NSPredicate predicateWithFormat:#"SELF.id == %d", eventTypeID];
NSArray *eventTypes = [[DataManager sharedInstance].managedObjectContext executeFetchRequest:eventTypesArray error:nil];
if(eventTypes.count==0)
{
DLog(#"ERROR inserting event with type: %i NOT FOUND",(int)eventTypeID);
return nil;
}
else {
if(eventTypes.count !=1)
{
DLog(#"ERROR found %i events with type %i",eventTypes.count,(int)eventTypeID);
}
EventType* eventType = [eventTypes lastObject];
if(date)
{
// DLog(#"Returning object");
return [self insertAndReturnNewObjectWithEventType:eventType date:date];
}else {
// DLog(#"Returning object");
return [self insertAndReturnNewObjectWithEventType:eventType];
}
}
}
Thank you for taking a look at my question!
The array of objects returned by a fetch request cannot be cached. They are only valid as long as the NSManagedObjectContext that was used to query them has not been released. The NsManagedObject.objectID and the data you retrieve from the query can be cached and kept for as long as you like. You are probably better off copying the pertinent data and objectIDs into another object you cache and maintain separately from CoreData objects; and releasing the core data array that was returned by the fetch request.
The pattern you're using is often referred to as "find or create": look for an object whose uniquing characteristic matches, return it if it exists, create/populate/return it if it didn't exist.
One thing you can do to speed this up is to do the uniquing outside of Core Data. If it's possible based on your data, perhaps you can iterate over your EventIDEnum values, find the unique values you need to have available, and thus reduce the number of fetches you perform. You'll only search once for each EventIDEnum. As long as you're working within one thread/context, you can cache those.
When I'm writing this kind of code, I find it helpful to pass in the NSManagedObjectContext as a parameter. That allows me to use the find-or-create or bulk insert methods anywhere, either on the main thread or within a private queue/context. That would take the place of your [[DataManager sharedInstance] managedObjecContext] call.

Core Data Wont Persist Property Updates to Database

I'm working with a subclass of NSManagedObject. Actually, it inherits from a class that inherits from a class that itself inherits from NSManagedObject (that shouldn't be a problem, right?).
The problem
After I make changes to the properties of the object, the object remembers the changes for its lifetime, but the changes are never saved to the database.
How Do I Know This?
I know this because:
when I restart the app, the changes I've made are lost.
telling the context to refresh the object – AFTER I've made changes to the object and told the context to save – sets the object's values back to their original state before I made the changes.
when running the app in the simulator, I can look at the sqlite database file in the Finder, and it's modified date isn't updated when I attempt to save the context.
Nothing is being written to the database!
Context
I'm using the auto-generated delegate methods to create the store coordinator and the context. Then I'm passing the context to the view controllers in their init methods, as recommended in the docs. The store is SQLite.
I am able to successfully insert objects into the database and read them. I can even make property changes to the newly inserted object and save it successfully. I simply don't seem to be able to update object properties when the object is pulled back out of the database.
The object is fetched from the store via a relationship from another object. After making changes to its properties, I call the context's save method. However, before doing so, I call the object's isUpdated method and the context's hasChanges method, and both return false. Shouldn't they return true since I've just made changes to the object's properties but haven't saved the context?
More
If I call the object's committedChanges method before saving the context, however, passing in the names of the properties that I've changed, I get back the correct values of the properties. I'm not sure what this means. I would have thought that this means that the object's new property values have been successfully saved, but clearly they are not saved.
I know that the result objects is registered with a context. If I call
[[result managedObjectContext] refreshObject:result mergeChanges:YES];
the object reverts back to the original property values. This means that the context is there and that it is the same context from which the record was fetched. And it means that the new property values are never written tot he database.
Some Code
Here's the code where I'm poking around with all of these things. There are other places in my code where I'm making property changes, but the changes are never saved.
- (IBAction)statusControlChanged:(UISegmentedControl *)control {
WCAAssessmentResult *result = [self currentResult];
/* printing the existing property values */
if (![result.complete boolValue]) NSLog(#"result is in progress!");
else if ([result.passed boolValue]) NSLog(#"result is passed!");
else NSLog(#"result is not passed!");
/* changing the property values */
switch (control.selectedSegmentIndex) {
case 0:
NSLog(#"setting incomplete");
result.complete = [NSNumber numberWithBool:NO];
break;
case 1:
NSLog(#"setting passed");
result.passed = [NSNumber numberWithBool:YES];
result.complete = [NSNumber numberWithBool:YES];
break;
case 2:
NSLog(#"setting failed");
result.passed = [NSNumber numberWithBool:NO];
result.complete = [NSNumber numberWithBool:YES];
break;
default:
break;
}
/* this method always returns an empty dictionary */
NSLog(#"%#", [result changedValues]);
/* this method returns the values that I just set */
NSLog(#"%#", [result committedValuesForKeys:[NSArray arrayWithObjects:#"complete", #"passed", nil]]);
/* isUpdated returns false */
if (![result isUpdated]) {
NSLog(#"result is not updated?! WTF!?!?");
}
/* hasChanges returns false */
if (![[result managedObjectContext] hasChanges]) {
NSLog(#"context has no changes!? WTF!?!?");
}
/* saving the context produces no error */
NSError *error = nil;
if (![[result managedObjectContext] save:&error]) {
NSLog(#"save failed");
NSLog(#"%#",[error description]);
}
}
A Twist
If I create a new result object by inserting a new record into the context, I can set that object's properties and they are saved successfully. In the above code, I'm fetching the object as a member of a to-many association from another object. Is that a clue?
I'm tearing my hair out over this. What the hell could be going wrong here?
What's NOT The Problem
I've logged the object's class, and it is indeed the correct class
I've made sure that the managedObjectContext I'm saving is the same as the object's context
I haven't made any changes to the auto-generated setter/getter methods of my managed object classes
I've tried using the setValue:forKey: method instead of object's properties
I've used the -com.apple.CoreData.SQLDebug 1 argument to log Core Data SQL, and no SQL is logged when I update and save the object's properties
I do not really understand your statement
WCAAssessmentResult *result = [self currentResult];
Indeed, if you are accessing a to-many relationship from an object, you should get back a set, not an object. Anyway, without seeing the code it's hard to tell. The problem you are experiencing may or may not lie there.
I would rather expect in your code something like the following snippet to access objects belonging to a to-many relationship. I assume that yourObject is the object you use to access the WCAAssessmentResult objects in the to-many relationship, which I call results.
NSMutableSet *resultObjects = [yourObject mutableSetValueForKey:#"results"];
NSPredicate *predicate = ...
[resultObjects filterUsingPredicate:predicate];
for(WCAAssessmentResult *result in resultObjects){
// modify as needed the current result object
}
NSError *error = nil;
if (![managedObjectContext save:&error]) {
NSLog(#"save failed");
NSLog(#"%#",[error description]);
}
Did you verify that the managedObjectContext you are using to save the object is valid (not nil) ?
Some ideas in no particular order:
I would log the class of the result object and make sure it is the class you think it is. Some confusion with super/sub classes could result in certain values not being saved.
If you made any alterations in the setter/getter methods in any class in the hierarchy, look closely at those methods especially if you used a primativeValue method. Simply leaving out willChangeValue and didChangeValue can cause changes to be invisible to the context and sometimes to the object itself.
I would log the context you are saving as well as managedObjectContext property of the result object. Confirm they are indeed the same context.
Skip using the property accessors (the dot notation) and use setValue:forKey and see if that makes any difference. If so, you have an accessor problem. Likewise, you could try setPrimativeValue:forKey to also check for an accessor problem.
If I had to bet, I would put my money on you assigning the objects returned by a fetch to the wrong class.

Is it still true that it's a bad idea to name attributes and relationships like any non-parameter method of NSObject or NSManagedObject?

I did some little experimentation on this.
I created an boolean attribute, named isFault. You know, that's a method of NSManagedObject and therefore actually not allowed for an attribute name because of KVC.
Simply, I used the default Core Data template for this test, but created the data model programmatically so I can show you what I do.
So here we go:
NSAttributeDescription *badAttr = [[NSAttributeDescription alloc] init];
[badAttr setName:#"isFault"];
[badAttr setAttributeType:NSBooleanAttributeType];
[badAttr setOptional:YES];
// don't want to occupy you with the whole, non-important rest ...
Next, I've modified the -insertNewObject method of the controller, added these lines:
// Assume: An managed object is created into the context, but not saved yet...
NSLog(#"isFault = %d", [newManagedObject isFault]); // 0 = NO
BOOL isFault = [[newManagedObject valueForKey:#"isFault"] boolValue];
NSLog(#"isFault = %d", isFault); // 0 = NO
[newManagedObject setValue:[NSNumber numberWithBool:YES] forKey:#"isFault"];
isFault = [[newManagedObject valueForKey:#"isFault"] boolValue];
NSLog(#"isFault = %d", isFault); // 0 = NO
Like you can see, I'm not able to set the isFault attribute to YES. It remains NO. Now, changing the attribute name to isFaultXYZ, will allow that.
So actually, what I wanted to ask is... since this stuff seems to depend on KVC, does the rule only apply to methods that return something and have no parameter? And does it matter what data type is returned? For example, -changedValues has no parameter and returns an NSDictionary. But since there is no attribute type like that, would this cause a collision anyways?
So, here is the evidence. When naming the attribute changedValues, THIS happens after attempting to access it via KVC:
*** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '*** -[NSCFDictionary boolValue]: unrecognized selector sent to instance 0x1205680'
2010-06-10 14:52:40.232 CoreData[33996:207] Stack: (
8035,
4437,
8825,