I have a core data many to many relationship of article to category. When attempting to save, I get the following error. I am unable to find information about what it means, and why there are two versions when the database is empty. Can anyone shed some light?
Error: NSMergeConflict (0x76ae720) for NSManagedObject (0xd945560) with objectID '0xd943550 <x-coredata://09A438A8-E3F5-45FE-B9D7-106798E82E18/Article/p91>' with oldVersion = 1 and newVersion = 2
Code:
NSMutableDictionary *dict = [[data objectAtIndex:i] valueForKey:#"category_names"];
NSMutableArray *values = [[NSMutableArray alloc] init];
for (NSString *value in [dict allValues]) {
NSLog(#"value = %#", value);
[values addObject:value];
}
NSMutableSet *setElements = [[NSMutableSet alloc] init];
for (int i = 0; i < [values count]; i++) {
Category *cat = [self getCategoryFor:[values objectAtIndex:i]]; // Function which has fetch to get the category for the value "name"
[setElements addObject:cat];
}
if ([setElements count] > 0)
[article addCategories:setElements];
// Save the context.
NSError* error;
if (![managedObjectContext save:&error]) {
NSLog(#"Failed to save to data store: %#", [error localizedDescription]);
NSArray* detailedErrors = [[error userInfo] objectForKey:NSDetailedErrorsKey];
if(detailedErrors != nil && [detailedErrors count] > 0) {
for(NSError* detailedError in detailedErrors) {
NSLog(#" DetailedError: %#", [detailedError userInfo]);
}
} else
NSLog(#" %#", [error userInfo]);
}
[article release];
[values release];
[setElements release];
The error your getting is not actually related to the data itself but rather caused by having two irreconcilable versions of the data model writing to the same persistent store file.
You must have created a data model, used it write some data to the persistent store and then updated the model. This is normally not an issue unless you so alter the data model that the automatic migration cannot merge the old and new data.
If this is still under development and you don't need the existing data, the easiest solution is to delete the app off the simulator and then build and run it again using only the latest data model. That will require no migration and will therefore skip the merge error.
Related
I saved my managedObjects. But my NSManagedObjectID is still temporary after saving. Why?
dispatch_async(privateQueue, ^{
__block NSMutableArray *ids = [NSMutableArray array];
[[[LPAppDelegate instance] privateContext] performBlockAndWait:^{
if ([responseObject isKindOfClass:[NSDictionary class]]) {
id arr = ((NSDictionary*)responseObject)[#"results"];
for (int i=0; i < ((NSArray *)arr).count; i++) {
LPFilm *film = [LPFilm MR_createEntityInContext:privateContext];
[ids addObject:film.objectID];
}
}
NSError *error = nil;
[privateContext save:&error];
if (error) {
NSLog(#"___fetch service error: %#", [error localizedDescription]);
}
}];
for (NSManagedObjectID *objID in ids) {
if (objID.isTemporaryID) {
NSLog(#"__tempID %#", objID);
}
}
});
When you save changes, new NSManagedObject instances get a new object ID. Previously existing instances of NSManagedObjectID are not converted in-place, they're replaced with new instances. But you have to ask the managed object for its new ID.
In your case, you're saving up an array of object IDs before saving. Those objects will never change, even if you save changes. But, if you go back to your managed objects and ask them for their object IDs again, you'll get different results, which will not be temporary.
Hi in my application I am setting the value for NSManagedObject while I am trying to set a value app is crashing.Here is the code and error message.
NSManagedObject *object3 = [threadManagedObjectContext objectWithID:[object1 objectID]] ;
for (int i=0;i<[array1 count];i++)
{
NSDictionary *keyValue=[array1 objectAtIndex:i];
[object3 setValue:[[keyValue allValues] lastObject] forKey:[[keyValue allKeys] lastObject]] ;
}
Error: Terminating app due to uncaught exception 'NSGenericException', reason:was mutated while being enumerated
Can any one please help me.
In your code (not the provided) , you're changing a collection while looping/iterating.
Example (not allowed):
for (MyClass *myClassObj in collectionOfMyClass) {
[myClassObj setClassVar: aVar]
}
Solution:
Make a temporary collection of the objects you want to set. Set all of them back outside of your loop.
NSMutableArray *tempArray = [[NSMutableArray alloc] init];
for (MyClass *myClassObj in collectionOfMyClass) {
[tempArray addObject:aVar];
}
[myClassObj setClassVars: tempArray];
You can try below solution for updating the object...
NSManagedObject *object3 = [threadManagedObjectContext objectWithID:[object1 objectID]] ;
int i=0;
for (NSDictionary *keyValue in array1)
{
[[object3 setValue:[[keyValue allValues] lastObject] forKey:[[keyValue allKeys] lastObject]] ;i++;
}
NSError *error;
bool result = [[fetchedResultsController threadManagedObjectContext] save:&error];
if (!result) {
NSLog(#" error saving context, %#, %#", error, error.userInfo);
}
In your code while Looping/Enumerating you are setting value..So it is crashing...!
Hope it helps you....
This is a SyncEngine from an RW tutorial. I need help understanding how only UPDATED records from the web are fetched and processed into Core Data.
- (void)processJSONDataRecordsIntoCoreData {
NSManagedObjectContext *managedObjectContext = [[SDCoreDataController sharedInstance] backgroundManagedObjectContext];
// Iterate over all registered classes --- CHECK!
for (NSString *className in self.registeredClassesToSync) {
if (![self initialSyncComplete]) {
NSDictionary *JSONDictionary = [self JSONDictionaryForClassWithName:className];
NSArray *records = [JSONDictionary objectForKey:#"results"];
for (NSDictionary *record in records) {
[self newManagedObjectWithClassName:className forRecord:record];
}
} else {
NSArray *downloadedRecords = [self JSONDataRecordsForClass:className sortedByKey:#"objectId"];
if ([downloadedRecords lastObject]) {
NSArray *storedRecords = [self managedObjectsForClass:className sortedByKey:#"objectId" usingArrayOfIds:[downloadedRecords valueForKey:#"objectId"] inArrayOfIds:YES];
int currentIndex = 0;
//if dl count is < current index, there is an updated object dl from the web
for (NSDictionary *record in downloadedRecords) {
NSManagedObject *storedManagedObject = nil;
//Quick check to see if they indeed match, if they do, update the stored object with remote service objects
if ([storedRecords count] > currentIndex) {
storedManagedObject = [storedRecords objectAtIndex:currentIndex];
}
//Othwerwise its a new object and you need to create a new NSManagedObject to represent it in CDdb
if ([[storedManagedObject valueForKey:#"objectId"] isEqualToString:[record valueForKey:#"objectId"]]) {
[self updateManagedObject:[storedRecords objectAtIndex:currentIndex] withRecord:record];
} else {
[self newManagedObjectWithClassName:className forRecord:record];
}
currentIndex++;
}
}
}
// After all NSMO are created in your context, save it!
[managedObjectContext performBlockAndWait:^{
NSError *error = nil;
if (![managedObjectContext save:&error]) {
NSLog(#"Unable to save context for class %#", className);
}
}];
// Cleanup time
[self deleteJSONDataRecordsForClassWithName:className];
[self executeSyncCompletedOperations];
}
[self downloadDataForRegisteredObjects:NO];
}
From what I understand, on the first or initial sync, it fetches JSONDictionaryForClassWithName which reads the downloaded data from disk and creates a newManagedObjectWithClassName.
My confusion is in the update else block. downloadedRecords is populated from JSONDataRecordsForClass which simply calls JSONDictionaryForClassWithName. Then it checks to see if there is at least 1 object in that array. If there is it does this:
NSArray *storedRecords = [self managedObjectsForClass:className sortedByKey:#"objectId" usingArrayOfIds:[downloadedRecords valueForKey:#"objectId"] inArrayOfIds:YES];
This fetches all managedObjectsForClass:sortedByKey which is below:
- (NSArray *)managedObjectsForClass:(NSString *)className sortedByKey:(NSString *)key usingArrayOfIds:(NSArray *)idArray inArrayOfIds:(BOOL)inIds {
__block NSArray *results = nil;
NSManagedObjectContext *managedObjectContext = [[SDCoreDataController sharedInstance] backgroundManagedObjectContext];
NSFetchRequest *fetchRequest = [NSFetchRequest fetchRequestWithEntityName:className];
NSPredicate *predicate;
if (inIds) {
predicate = [NSPredicate predicateWithFormat:#"objectId IN %#", idArray];
} else {
predicate = [NSPredicate predicateWithFormat:#"NOT (objectId IN %#)", idArray];
}
[fetchRequest setPredicate:predicate];
[fetchRequest setSortDescriptors:[NSArray arrayWithObject:
[NSSortDescriptor sortDescriptorWithKey:#"objectId" ascending:YES]]];
[managedObjectContext performBlockAndWait:^{
NSError *error = nil;
results = [managedObjectContext executeFetchRequest:fetchRequest error:&error];
}];
return results;
}
The next bit which compares the [storedRecords count] > currentIndex is confusing. Can someone please explain this? I think my confusion lies in what the managedObjectsForClass method does with the usingArraysOfIds & inArrayOfIds.
I would expect that at some point it gets the the updatedAt field from the downloaded records and compares it to the updatedAt field of the CoreData fetched records.
This function is processing the stored JSON. The actual remote fetching and updateAt checking happens in downloadDataForRegisteredObjects and mostRecentUpdatedAtDateForEntityWithName.
[storedRecords count] > currentIndex is a bit crazy. Although in defense of the original programmer, writing any decent syncengine will quickly make you go googoo. Basically he needs to work out which records are existing and which ones are new and update the local data store accordingly, that's all.
I had another look and this code is actually horribly broken. It will only works if either you have the same records both locally and remotely. Or if the new objects have an objectId that sort-wise comes after the last object the local store has. Which is not the case with Parse objectId's.
If you are testing with just one device this works because new objects will be inserted locally before being pushed to the server. Therefor you will always have the same amount of records. If additional records get inserted any other way, this code will do weird things.
I have a case where I should insert object into an entity via UIViewController. I have designed my database model (Entity and attributes). I'm adding the entity through a UIViewController. What am I supposed to add in the didFinishLaunchingwithOptions method in appDelegate.m?
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
// Override point for customization after application launch
return YES;
}
And for the TUTViewController (My own view controller - UIViewController) I have used the below code for inserting object.
- (void)connectionDidFinishLoading:(NSURLConnection *)connection {
NSManagedObjectContext *context = [self.fetchedResultsController managedObjectContext];
NSEntityDescription *entity = [[self.fetchedResultsController fetchRequest] entity];
NSManagedObject *newManagedObject = [NSEntityDescription insertNewObjectForEntityForName:[entity name] inManagedObjectContext:context];
NSString *stripped1 = [response stringByReplacingOccurrencesOfString:#"\r" withString:#""];
NSMutableArray *rows = [NSMutableArray arrayWithArray:[stripped1 componentsSeparatedByString:#"\n"]];
NSMutableArray *contentArray = [NSMutableArray arrayWithCapacity:[rows count]];
NSMutableArray *contentArray1 = [NSMutableArray arrayWithCapacity:[rows count]];
NSArray *components;
NSLog(#"count:%d",[rows count]);
for (int i=0;i<[rows count]; i++) {
if(i == 0 || [[rows objectAtIndex:i] isEqualToString:#""]){
continue;
}
components = [[rows objectAtIndex:i] componentsSeparatedByString:#","];
id x = [components objectAtIndex:0] ;
id y = [components objectAtIndex:1];
id z = [components objectAtIndex:2];
[contentArray addObject:[NSMutableDictionary dictionaryWithObjectsAndKeys:x,#"X",y,#"Y", nil]];
[contentArray1 addObject:[NSMutableDictionary dictionaryWithObjectsAndKeys:x,#"X",z,#"Y", nil]];
[newManagedObject setValue:[x] forKey:#"timeStamp"];
[newManagedObject setValue:[y] forKey:#"beat"];
[newManagedObject setValue:[z] forKey:#"rate"];
// Save the context.
NSError *error = nil;
if (![context save:&error]) {
NSLog(#"Unresolved error %#, %#", error, [error userInfo]);
abort();
NSLog(#"Contents of Uterus Contraction: %#",contentArray);
NSLog(#"Contents of Heart Beat: %#",contentArray1);
}
}
}
Is there anything that I'm missing? I'm ending up with the error:
Terminating app due to uncaught exception
'NSInternalInconsistencyException', reason: '+entityForName: could not
locate an NSManagedObjectModel for entity name 'FetalData''
Did you set up the Core Data Stack or a UIManagedDocument object?
If you didn't set up the Managed Object Model this could be the problem. It means you're probably not loading Managed Object Model that defines FetalData entity. See insertnewobjectforentityforname for further info.
I really suggest to create an empty project and let Xcode to create Core Data stuff for you. In this manner you can see how the code works.
Some Notes
Why do you use a NSFetchResultsController?
Move the save call at the end of your method. In this manner you avoid multiple round trips to the disk.
If you want to start using Core Data, I suggest you core-data-on-ios-5-tutorial-getting-started.
Hope it helps.
I'm making the app using core-data.
It's facebook app, so I saved the User information to core-data.
And I wanna add the profile picture to user information data.
for (UserInfo *info in fetchedObjects) {
if ([info.fbId isEqualToString:delegate.currentUserInfo.fbId]) {
info.details.photos.image = [NSData dataWithContentsOfURL:url];
info.details.photos.imageId = [result objectForKey:#"id"];
info.details.photos.details = info.details;
delegate.currentUserInfo = info;
for (UserInfo *info in fetchedObjects) {
NSLog(#"currentUserInfo : %#", info);
NSLog(#"curretnUserDetails : %#", info.details);
NSLog(#"currentUserPhoto : %#", info.details.photos);
}
if (![context save:&error]) {
NSLog(#"Whoops, couldn't save: %#", [error localizedDescription]);
}
}
}
Like this, I add the data to the core-data (relationshop named photos) corresponding to FB user ID.
As a result, NSLog in the middle shows well photoInfo data, but when I restart my app, there is no photo data.
I didn't solve this problem for a few days. Help me!
-edited
DataModel is organized like this picture.
I'm using only one context, and I'm novice so I can't understand all of thankful comment.
-edited2
if (!beExist) {
UserInfo *userInfo = [NSEntityDescription insertNewObjectForEntityForName:#"UserInfo" inManagedObjectContext:context];
userInfo.name = [myInfoDic objectForKey:#"name"];
userInfo.fbId = [myInfoDic objectForKey:#"id"];
userInfo.type = #"user";
UserDetails *userDetails = [NSEntityDescription insertNewObjectForEntityForName:#"UserDetails" inManagedObjectContext:context];
userDetails.gender = [myInfoDic objectForKey:#"gender"];
userDetails.work = [[[[myInfoDic objectForKey:#"work"] objectAtIndex:0] objectForKey:#"employer"] objectForKey:#"name"];
NSArray *eduArray = [myInfoDic objectForKey:#"education"];
userDetails.education = [[[eduArray objectAtIndex:eduArray.count-1] objectForKey:#"school"] objectForKey:#"name"];
userDetails.birthday = [myInfoDic objectForKey:#"birthday"];
userDetails.location = [[myInfoDic objectForKey:#"location"] objectForKey:#"name"];
PhotoInfo *photoInfo = [NSEntityDescription insertNewObjectForEntityForName:#"PhotoInfo" inManagedObjectContext:context];
userDetails.info = userInfo;
userInfo.details = userDetails;
userInfo.details.photos = photoInfo;
photoInfo.details = userDetails;
delegate.currentUserInfo = userInfo;
if (![context save:&error]) {
NSLog(#"Whoops, couldn't save: %#", [error localizedDescription]);
}
I saved User information like this.
I created context in AppDelegate. I made the project for Core data, so Xcode made that code.
I can see this log
2012-05-04 00:40:58.997 Feek[16423:fb03] currentUserPhoto : <PhotoInfo: 0x6d15d60> (entity: PhotoInfo; id: 0x6d18550 <x-coredata://2ED200FE-D799-4C66-A0AC-5BE1BC426439/PhotoInfo/p1> ; data: {
details = "0x8830ca0 <x-coredata://2ED200FE-D799-4C66-A0AC-5BE1BC426439/UserDetails/p1>";
image = <ffd8ffe0 00104a46 49460001 02000001 00010000 fffe0004 2a00ffe2 021c4943 435f5052 4f46494c 45000101 0000020c 6c63>;
imageId = 334168116641714;
})
I think that this is core data accept data, but it doesn't remain after killing my app.