I experimenting NSIncrementalStore and documentation says "First, the persistent store coordinator invokes obtainPermanentIDsForObjects:error: .. then, the coordinator invokes executeRequest:withContext:error: "
In my case obtainPermanentIDsForObjects method is not get called, but executeRequest will get called with NSSaveRequestType argument. What is the problem?
I created a NSManagedObject and called the save method.
I tried to add a local and a remote store to coordinator, and this caused to block the method call. So I just removed the first line.
[_persistentStoreCoordinator addPersistentStoreWithType:NSSQLiteStoreType configuration:nil URL:storeURL options: #{NSSQLitePragmasOption : #{#"journal_mode" : #"DELETE"}} error:&error];
[_persistentStoreCoordinator addPersistentStoreWithType:#"CustomIncrementalStore" configuration:nil URL:nil options:nil error:&error];
Related
I am migrating my connection classes to support Caching via CoreData. RestKit does it automagically using RKEntityMapping. RestKit and CoreData did the mapping and validation correctly, I got a bunch of NSManagedObject I requested for but it is not saving the objects to the CoreData. When I check the logs, I got this.
W restkit.network.core_data:RKManagedObjectRequestOperation.m:776 Saving of managed object context failed, but a `nil` value for the `error` argument was returned. This typically indicates an invalid implementation of a key-value validation method exists within your model. This violation of the API contract may result in the save operation being mis-interpretted by callers that rely on the availability of the error.
E restkit.network.core_data:RKManagedObjectRequestOperation.m:818 Failed saving managed object context to the persistent store <NSManagedObjectContext: 0x145e5ce0>: (null)
I don't have an idea, why is this occurring. I checked my Model for wrong settings or attributes but I can't find any. As I observed, NSManagedObjects is saved InMemory (NSFetchResultsController was still able to fetch the objects even when I leave the view. So I guess, the objects were saved InMemory). Can someone help me find what is wrong here. Leaving any tips for debugging the problem is welcome. Thank you.
I had this code to setup the core data.
- (void)setupCoreData
{
NSManagedObjectModel *managedObjectModel = [[AppDelegate sharedDelegate] managedObjectModel];
RKManagedObjectStore *managedObjectStore = [[RKManagedObjectStore alloc] initWithManagedObjectModel:managedObjectModel];
[[RKObjectManager sharedManager] setManagedObjectStore:managedObjectStore];
[managedObjectStore createPersistentStoreCoordinator];
NSString *storePath = [RKApplicationDataDirectory() stringByAppendingPathComponent:#"Model.sqlite"];
NSError *error;
NSPersistentStore *persistentStore = [managedObjectStore addSQLitePersistentStoreAtPath:storePath fromSeedDatabaseAtPath:nil withConfiguration:nil options:#{NSMigratePersistentStoresAutomaticallyOption:#YES, NSInferMappingModelAutomaticallyOption:#YES} error:&error];
NSAssert(persistentStore, #"Failed to add persistent store with error: %#", error);
// Create the managed object contexts
[managedObjectStore createManagedObjectContexts];
// Configure a managed object cache to ensure we do not create duplicate objects
managedObjectStore.managedObjectCache = [[RKInMemoryManagedObjectCache alloc] initWithManagedObjectContext:managedObjectStore.persistentStoreManagedObjectContext];
}
This is called before I add all response/request descriptors. I generated the model class using MoGenerator. I override some setters and did some validations before inserting (validateForInsert).
thanks for reading. We've recieved crash reports on our iOS app with the following error:
*** Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'This NSPersistentStoreCoordinator has no persistent stores. It cannot perform a save operation.'
This occurs when our main queue NSManagedObjectContext attempts to save:
BOOL saved = [managedObjectContext save:&error];
Which occurs in a block submitted via:
[managedObjectContext performBlockAndWait:saveBlock];
The internet (especially SO) is full of explanations for this error suggesting that we never had a persistent store cooridinater (that we failed to create it properly when we built the core data stack). However, it's difficult to see how we could get to this point in program execution without a persistent store coordinator. For kicks, i commented out the line where we set the persistent store and the app crashes almost immediately (without getting to our save method).
Does anyone know if there's an alternate cause for this exception or is it ALWAYS because there is no persistent store? Is there any way to lose a persistent store?
A little more color: we use parent/child NSManagedObjectContexts in order to load data in a background thread. The full save method looks like this:
-(void)saveWithManagedObjectContext:(NSManagedObjectContext*)managedObjectContext successHandler:(void (^)())successHandler failureHandler:(void (^)(NSManagedObjectContext* managedObjectContext, NSError*))failureHandler
{
void(^saveBlock)() = ^() {
NSError *error = nil;
BOOL saved = [managedObjectContext save:&error];
if (!saved) {
if (error != nil) {
NSLog(#"Unresolved error saving %#, %#", error, [error userInfo]);
}
failureHandler(managedObjectContext, error);
} else {
if (managedObjectContext.parentContext != nil) {
[self saveWithManagedObjectContext: managedObjectContext.parentContext successHandler:successHandler failureHandler:failureHandler];
} else {
successHandler();
}
}
};
BOOL isMainContext = managedObjectContext.parentContext == nil;
if (isMainContext) {
[managedObjectContext performBlockAndWait:saveBlock];
} else {
[managedObjectContext performBlock:saveBlock];
}
}
It can be called from a background thread which will call save: on the NSManagedObjectContext via a performBlock: and then rescursively call this method on the parent NSManagedObjectContext in order to save it. When the app crashes, it's always on the main thread which makes sense because the main queue context is the only one that needs a persistent store.
Many thanks for any help. I did cross post to devforums.apple.com, so my apologies if you've seen this twice.
There are basically two places to look.
First, check what happens when you first add the persistent store to the Core Data stack. That would be in the app delegate if you are using the Apple template, but typically somewhere in your code when the app is initialized there is a call to
addPersistentStoreWithType:configuration:URL:options:error:.
Second, as you mention background threads and because you are passing a managed object context to your method, you possibly have child contexts. Check that you have properly assigned either a valid parent context or the store coordinator to the child context when you create it.
This error is unfortunately ambiguous. I have seen it when using configurations and there is a typo in the configuration name or in one case where a configuration name was passed in when there were no configurations in the model.
So my first line of testing would be to look at your Core Data stack creation code. Can you add that to your question so we can take a look at it?
I corresponded offline with Tom Harrington who made the innocuous comment: "Persistent stores shouldn't go away unless you remove them (and I'm assuming you don't do that)." 'course i DO do that when i tear down the core data stack (when a user logs out.) When i tear down the stack, i call reset on my root managed object context (the only one i have a reference to) and then remove the persistent store. However, if there are pending changes in a child context, they'll propagate up to my now persistentstoreless root context causing the crash on save. Since there's a good reason why parent contexts don't keep track of their children, i don't want to keep track of them either. Instead i just confirm that there is either a parent context or a persistent store coordinator (with at least one store) before calling save.
I am working on a app that is going to require the use of Core Data and I can't help but notice that Core Data has to be put in manually if you use anything but the Master-Detail, Utility or Blank templates in Xcode.
I also noticed that in order for Core Data to work properly, you HAVE to have your app wrapped in a Navigation Controller and Core Data's code in the AppDelegate files.
Anyone know of a way around this or is this the way it is supposed to be?
My App Delegate looks something like this and those three lines seem to be the most important setup for the ManagedObjectContext!
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
// Override point for customization after application launch.
UINavigationController *navController = (UINavigationController *)self.window.rootViewController;
FBBetsViewController *controller = (FBBetsViewController *)navController.topViewController;
controller.managedObjectContext = self.managedObjectContext;
Those templates include some core data setup, but it is far from mandatory. You can use core data from within any project. If you want, you can just take the code from the empty application, and use it in your project.
If you look in the generated code, you will see three "getters" for the three main components used to build the core data stack.
managedObjectModel creates the model, by using the model file from your bundle. Create that easily in Xcode by New-File and selecting the Core Data Data Model.
persistentStoreCoordinator uses the model, and a SQL store.
Finally, managedObjectContext is created by using the persistentStoreCoordinator. Note, you really can build that stack in one method if you want. There is no requirement to have those individual accessors...
You could do something like this...
- (NSManagedObjectContext*)setupCoreDataStack
{
// Load the model description
NSURL *modelURL = [[NSBundle mainBundle] URLForResource:#"APPNAME" withExtension:#"momd"];
NSManagedObjectModel *model = [[NSManagedObjectModel alloc] initWithContentsOfURL:modelURL];
// Prepare the persistent store coordinator - needs the model
NSURL *documentsDirectory = [[[NSFileManager defaultManager] URLsForDirectory:NSDocumentDirectory inDomains:NSUserDomainMask] lastObject];
NSURL *storeURL = [applicationDocumentsDirectory URLByAppendingPathComponent:#"APPNAME.sqlite"];
NSError *error = nil;
NSPersistentStoreCoordinator *psc = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel:model];
if (![psc addPersistentStoreWithType:NSSQLiteStoreType configuration:nil URL:storeURL options:nil error:&error])
{
// Handle the error !!!!!
// exit the function
return nil;
}
// Create the managed object context. This is what you will really
// use in the rest of your program.
NSManagedObjectContext *moc = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSMainQueueConcurrencyType];
[moc setPersistentStoreCoordinator:psc];
return moc;
}
And now you have almost the same stack as the others. The only real difference is that the MOC here is using the main queue concurrency type, which is a much better alternative.
If you want to have a much better performance model, insert a parent moc.
Actually, if you are not married to a current cored data strategy, I'd suggest UIManagedDocument.
Core Data does not impose that you use a navigation controller nor that you set it up in the AppDelegate. It's customary to put the setup in the AppDelegate upon startup but really, you can move it wherever you want as long as you make sure it's only initialized once.
Dear community.
I try to discover opportunity to using 2 persistent stores for improve performance of my application.
What i do here:
CREATE 2 PERSISTENT STORES
persistentStoreCoordinator = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel:mom];
NSMutableDictionary *dict = [NSMutableDictionary dictionary];
[dict setObject:[NSNumber numberWithBool:YES]
forKey:NSMigratePersistentStoresAutomaticallyOption];
if (![persistentStoreCoordinator addPersistentStoreWithType:NSInMemoryStoreType
configuration:nil
URL:[NSURL URLWithString:#"memory://store"]
options:dict
error:&error])
{
[[NSApplication sharedApplication] presentError:error];
[persistentStoreCoordinator release], persistentStoreCoordinator = nil;
return nil;
}
if (![persistentStoreCoordinator addPersistentStoreWithType:NSSQLiteStoreType
configuration:nil
URL:url
options:dict
error:&error])
{
[[NSApplication sharedApplication] presentError:error];
[persistentStoreCoordinator release], persistentStoreCoordinator = nil;
return nil;
}
ASSIGN new created objects to in-Memory store
NSManagedObject *objectCarrier = [NSEntityDescription
insertNewObjectForEntityForName:#"Carrier"
inManagedObjectContext:managedObjectContext];
[objectCarrier setValue:startForCarrier
forKey:#"name"];
NSURL *url = [NSURL URLWithString:#"memory://store"];
[managedObjectContext assignObject:objectCarrier
toPersistentStore:[[appDelegate persistentStoreCoordinator] persistentStoreForURL:url]];
SAVE FINAL OBJECT
A difference between in-memory and particular persistent store using is
i have wrong using objects from predicates for same code.
If i just change persistent store type, i pickup object:
NSManagedObject *destination = [[codeAfterComparing lastObject] valueForKey:codeRelationshipName];
But set values for this object is doesn't work.
If i try to assignObject for received object, i have error (it's doesnt matter, how this object was save as inMemory or asSqlLite store object, error is start every time).
2011-02-16 14:32:45.037 snow
server[44411:1803] * Terminating app
due to uncaught exception
'NSInvalidArgumentException', reason:
'Can't reassign an object to a
different store once it has been
saved.'
Attempt to save a final object's graph with two different stores gives me error "CoreData does not support persistent cross-store relationships", and it's doesn't matter, where cureent object assing.
Migration was as :
for (NSPersistentStore *persistentStore in [persistentStoreCoordinator persistentStores]) {
if (persistentStore.type == NSInMemoryStoreType) {
// migrate the in-memory store to a SQLite store
NSError *error;
[persistentStoreCoordinator migratePersistentStore:persistentStore toURL:[NSURL fileURLWithPath:[[self applicationSupportDirectory] stringByAppendingPathComponent:#"storedata.sql"]] options:nil withType:NSSQLiteStoreType error:&error];
if (! newPersistentStore) {
Product error: "Can't add the same store twice"
So, the result is a very strange for me:
1. Looks like managed object context have no difference for objects between 2 stores. If i ask save, it take whole object and save so same sqlite store
2. maybe a way to using different persistent store coordinator's but i don't know exactly, how is easy transfer objects between 2 stores. Of course, i can do a copy (include relationships e.t.c.) but this is a hard code for this simple issue, i guess.
Maybe somebody can suggest about my code wrong or good working examples of code to review and understand a good way to do in memory cache with core data? Google search gives not too much examples.
If you look at the Core Recipe example code on Apple's website, they use multiple stores to save objects in memory and on disk.
Thought I'd take a stab here.
I've had this problem in the past. I ended up removing the functionality for two persistent stores in the same coordinator. If I understand Apple correctly, Object Entities cannot be shared between persistent stores. So to make things easier, I usually just do the following (though I suspect there is an efficiency issue with using an additional Coordinator)
1 NSPersistentStore per NSPersistentStoreCoordinator
break up the scratchpad work to the NSManagedObjectContexts
create a deep-copy method to your NSManagedObject subclasses
And then, when whatever class you have managing each persistent store utilize the copy function to import the managed objects.
I can't really think of an instance where you'd want to go through the extra trouble of individually assigning the managed objects to a specific store that wouldn't be taken car of in this way.
I have a program that utilizes two stores - one in memory for transient objects and another managing the document. It's working just fine.
In iOS 5 Apple introduce Nested Managed Object Contexts where you can work with two Managed Object contexts.
This may replace your approach with the in memory store because e.g. you can now use one of the (also) new concurrency types to run one context in the background (e.g. for background fetching) and another as your main context.
Take a look in the WWDC2011 Session 303.
I would like to use Core Data Managed Objects outside of a managed object context. I've seen other threads on this site that say you should never do this, but here's my issue:
I have a 'Feed' object and a 'story' object. Feed is like an RSS feed, and story is like a single story from that feed. I have the ability to bookmark feeds, and I use Core Data to persist those, but I when I download stories from a feed, I don't want to insert those stories into the managed object context. The only way to create my objects, however, is by doing this:
[NSEntityDescription insertNewObjectForEntityForName:name inManagedObjectContext:managedObjectContext];
Which means that it will be persisted at the next save event.
I don't want these objects to be persisted until the user selects them.
I tried defining a "TransientStory" and a "PersistentStory" with a protocol called "Story" that both of them implement, but it's a nightmare. Any ideas?
You can create these objects and just not insert them in the context:
NSEntityDescription *entity = [NSEntityDescription entityForName:entityName
inManagedObjectContext:managedContext];
ManagedObjectClass *volatileObject = [[ManagedObjectClass alloc] initWithEntity:entity
insertIntoManagedObjectContext:nil];
And if you want to save it you just insert it to the context:
[managedContext insertObject:volatileObject];
(if you forget to add it, it will give you a dangling object error when you try to save it in the context)
Create a new NSManagedObjectContext with an in-memory store. Then you can put your transient objects into this context, and they won't be persisted.
NSManagedObjectModel *mom = [NSManagedObjectModel mergedModelFromBundles:[NSArray arrayWithObject:[NSBundle mainBundle]]]; // though you can create a model on the fly (i.e. in code)
NSPersistentStoreCoordinator *psc = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel:mom];
NSError *err;
// add an in-memory store. At least one persistent store is required
if([psc addPersistentStoreWithType:NSInMemoryStoreType configuration:nil URL:nil options:nil error:&err] == nil) {
NSLog(#"%#",err);
}
NSManagedObjectContext *moc = [[NSManagedObjectContext alloc] init];
[moc setPersistentStoreCoordinator:psc];
If you did want to then persist them, just move them to the proper store afterwards, or merge the context.
Alternatively, if you're eventually going to put them into that context anyway (i.e. you just don't want them appearing in lists until they're saved), then just set setIncludesPendingChanges to NO in your NSFetchRequest.