Possible to use two separate SQLite databases? - iphone

I have one sqlite database in which I store both user-defined information and information which is read-only to the user. I feel like I may need to modify the read-only information in the future, and I don't want to have to do a whole data migration. Is there a way that I can use a separate sqlite database, which can easily be replaced, for the read-only information? If so, can you give a little direction as to how this can be done? I am confused since I currently have all entities on the xcdatamodel - would I create two data models? Not sure how that would work. Thanks in advance.
This doesn't work but please feel free to give feedback.
- (NSManagedObjectModel *)managedObjectModel {
NSLog(#"%s", __FUNCTION__);
if (managedObjectModel != nil) {
return managedObjectModel;
}
//managedObjectModel = [[NSManagedObjectModel mergedModelFromBundles:nil] retain];
NSString *mainPath = [[NSBundle mainBundle] pathForResource:#"MyApp" ofType:#"mom"];
NSURL *mainMomURL = [NSURL fileURLWithPath:mainPath];
managedObjectModel = [[NSManagedObjectModel alloc] initWithContentsOfURL:mainMomURL];
[managedObjectModel setEntities:[NSArray arrayWithObjects:
[[managedObjectModel entitiesByName] objectForKey:#"Version"],
[[managedObjectModel entitiesByName] objectForKey:#"Book"],
nil] forConfiguration:#"info"];
[managedObjectModel setEntities:[NSArray arrayWithObjects:
[[managedObjectModel entitiesByName] objectForKey:#"Settings"],
[[managedObjectModel entitiesByName] objectForKey:#"Persons"],
nil] forConfiguration:#"main"];
return managedObjectModel;
}
and
- (NSPersistentStoreCoordinator *)persistentStoreCoordinator {
NSLog(#"%s", __FUNCTION__);
if (persistentStoreCoordinator != nil) {
return persistentStoreCoordinator;
}
NSString *storePath = [[self applicationDocumentsDirectory] stringByAppendingPathComponent: #"Main.sqlite"];
NSFileManager *fileManager = [NSFileManager defaultManager];
if (![fileManager fileExistsAtPath:storePath]) {
NSString *defaultStorePath = [[NSBundle mainBundle] pathForResource:#"Default" ofType:#"sqlite"];
if (defaultStorePath) {
[fileManager copyItemAtPath:defaultStorePath toPath:storePath error:NULL];
}
}
NSURL *storeUrl = [NSURL fileURLWithPath:storePath];
NSDictionary *options = [NSDictionary dictionaryWithObjectsAndKeys:[NSNumber numberWithBool:YES], NSMigratePersistentStoresAutomaticallyOption, [NSNumber numberWithBool:YES], NSInferMappingModelAutomaticallyOption, nil];
NSString *infoStorePath = [[self applicationDocumentsDirectory] stringByAppendingPathComponent: #"Info.sqlite"];
if (![fileManager fileExistsAtPath:infoStorePath]) {
NSString *defaultInfoStorePath = [[NSBundle mainBundle] pathForResource:#"DefaultInfo" ofType:#"sqlite"];
if (defaultInfoStorePath) {
[fileManager copyItemAtPath:defaultInfoStorePath toPath:infoStorePath error:NULL];
}
}
NSURL *infoStoreUrl = [NSURL fileURLWithPath:infoStorePath];
persistentStoreCoordinator = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel: [self managedObjectModel]];
//persistentStoreCoordinator = [[NSPersistentStoreCoordinator alloc] init];
NSPersistentStore *mainStore = [persistentStoreCoordinator addPersistentStoreWithType:NSSQLiteStoreType configuration:#"main" URL:storeUrl options:options error:&error];
NSPersistentStore *infoStore = [persistentStoreCoordinator addPersistentStoreWithType:NSSQLiteStoreType configuration:#"verses" URL:infoStoreUrl options:options error:&error];
NSManagedObject *settingsEntity = [[NSManagedObject alloc] initWithEntity:[[managedObjectModel entitiesByName] objectForKey:#"Settings"] insertIntoManagedObjectContext:self.managedObjectContext];
[self.managedObjectContext assignObject:settingsEntity toPersistentStore:mainStore];
NSManagedObject *persons = [[NSManagedObject alloc] initWithEntity:[[managedObjectModel entitiesByName] objectForKey:#"Persons"] insertIntoManagedObjectContext:self.managedObjectContext];
[self.managedObjectContext persons toPersistentStore:mainStore];
NSManagedObject *version = [[NSManagedObject alloc] initWithEntity:[[managedObjectModel entitiesByName] objectForKey:#"Version"] insertIntoManagedObjectContext:self.managedObjectContext];
[self.managedObjectContext assignObject:version toPersistentStore:infoStore];
NSManagedObject *book = [[NSManagedObject alloc] initWithEntity:[[managedObjectModel entitiesByName] objectForKey:#"Book"] insertIntoManagedObjectContext:self.managedObjectContext];
[self.managedObjectContext assignObject:book toPersistentStore:infoStore];
and
- (NSManagedObjectContext *)managedObjectContext {
NSLog(#"%s", __FUNCTION__);
if (managedObjectContext != nil) {
return managedObjectContext;
}
NSPersistentStoreCoordinator *coordinator = [self persistentStoreCoordinator];
if (coordinator != nil) {
managedObjectContext = [NSManagedObjectContext new];
[managedObjectContext setPersistentStoreCoordinator: coordinator];
}
return managedObjectContext;
}

Partial answer from docs:
http://developer.apple.com/library/ios/#documentation/cocoa/Conceptual/CoreData/Articles/cdMOM.html
Configurations
A configuration has a name and an
associated set of entities. The sets
may overlap—that is, a given entity
may appear in more than one
configuration. You establish
configurations programmatically using
setEntities:forConfiguration: or using
the Xcode data modeling tool (see
Xcode Tools for Core Data), and
retrieve the entities for a given
configuration name using
entitiesForConfiguration:.
You typically use configurations if
you want to store different entities
in different stores. A persistent
store coordinator can only have one
managed object model, so by default
each store associated with a given
coordinator must contain the same
entities. To work around this
restriction, you can create a model
that contains the union of all the
entities you want to use. You then
create configurations in the model for
each of the subsets of entities that
you want to use. You can then use this
model when you create a coordinator.
When you add stores, you specify the
different store attributes by
configuration. When you are creating
your configurations, though, remember
that you cannot create cross-store
relationships.
Then NSPersistentStoreCoordinator allows you to create multiple stores each with a different configuration.
Anyone have an example of how to do all of this?

You can actually use a single data model to accomplish this, however you'll need to manually (in code) assign entities to different NSPersistentStore instances, a little bit of code:
NSPersistentStoreCoordinator *coord = [[NSPersistentStoreCoordinator alloc] init];
NSPersistentStore *userStore = [coord addPersistentStoreWithType:NSSQLiteStore configuration:nil URL:someFileURL options:someoptions error:&error];
NSPersistentStore *otherStore = [coord addPersistentStoreWithType:NSSQLiteStore configuration:nil URL:someFileURL2 options:someoptions error:&error];
//Now you use the two separate stores through a managed object context that references the coordinator
NSManagedObjectContext *context = [[NSManagedObjectContext alloc] init];
[context setPersistentStoreCoordinator:coord];
NSManagedObject *userObject = [[NSManagedObject alloc] initWithEntity:entityDescFromModel insertIntoManagedObjectContext:context];
[context assignObject:userObject toPersistentStore:userStore];
NSManagedObject *otherObject = [[NSManagedObject alloc] initWithEntity:entityDescFromModel insertIntoManagedObjectContext:context];
[context assignObject:otherObject toPersistentStore:otherStore];
In this way you can always specify which store the objects are kept in. I don't think you'll have to do any extra work once the objects are in their respective stores, i.e. you should just be able to execute a fetch spec in the context that references the coordinator for both stores.

Well, here's what I ended up doing. Two managedObjectModels, two managedObjectContexts, two persistentStoreCoordinators, and hence, two persistent stores. All totally separate, which is fine, since there is no relationship between the data in the two stores at all. And here is the kicker as to why sqlite files get created with no entities and no data at all: before the entities even get created you need to execute at least one fetch request in the db. Who knew? Well, obviously, not me. Anyway, this works well for me, as I won't even have the second store ready until after the app is launched (it is for an additional feature). Now, when my data file is finally ready, I can just add the sqlite file, uncomment the code pertaining to it, and send the app to the app store. It won't touch the store with the user data in it. And, I am going to keep my read-only store in my bundle so no migration. How's that sound?

Ok, I found out how to add another data model. File>New File>Iphone OS>Resource>Data Model. Moved my Entities to that Data Model. Compiled and seems to run, but with no data. Problem is, still have just one sqlite file. Need to find out how to use two, and associate each with appropriate model. Then, should be able to overwrite default sqlite file for new model on app update. BUT I will still have to do a migration, I think, since it will have created a sqlite file on the iPhone from the default file I specify. It shouldn't be a hard migration I hope since I won't have any user data in the file. Learning, but still, any further assistance appreciated.

Related

Core data update model with app update?

everyone who work with Core Data know the message "the model used to open the store is incompatible with the one used to create the store".
Then I have to delete my app from simulator, and rebuilding it again.
My question is if I submit an app v 1.0, then add some entities to core data in v 1.1, does this mean that the users of 1.0 who updated to 1.1 will have their data cleared up?
You will need to create a new model version for your model, and migrate the database. You can do a lightweight migration if your model changes are within the required changes. If not, you will need to tell core data how to migrate your data. Check the migration documentation: http://developer.apple.com/library/mac/#documentation/cocoa/conceptual/CoreDataVersioning/Articles/Introduction.html
In your case it sounds like a simple extension to your old data model. If you just really add some new entities or even new classes then the so called leightweight migration is the right way to go for you.
Actually in this case you almost do not have anything to do, but create your second model IN ADDITION to your original model. It is important, that you have BOTH model, then the app will just load your 1st version without any problems as well as the new version.
Don't forget to mark your new model as the new one!
Try to be careful when creating the new model, since deleting a model is a real hassle.
Your code will look very similar to this:
-(NSManagedObjectContext *)managedObjectContext {
if (managedObjectContext != nil) {
return managedObjectContext;
}
NSPersistentStoreCoordinator *lC = [self persistentStoreCoordinator];
if (lC != nil) {
managedObjectContext =[[NSManagedObjectContext alloc] init];
[managedObjectContext setPersistentStoreCoordinator: lC];
}
return managedObjectContext;
}
- (NSPersistentStoreCoordinator *) persistentStoreCoordinator {
if (persistentStoreCoordinator != nil) {
return persistentStoreCoordinator;
}
persistentStoreCoordinator = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel: [self managedObjectModel]];
// Allow inferred migration from the original version of the application.
NSDictionary *options = [NSDictionary dictionaryWithObjectsAndKeys:
[NSNumber numberWithBool:YES], NSMigratePersistentStoresAutomaticallyOption,
[NSNumber numberWithBool:YES], NSInferMappingModelAutomaticallyOption, nil];
NSURL *storeUrl = [NSURL fileURLWithPath: [[self applicationDocumentsDirectory] stringByAppendingPathComponent: #"DBName.sqlite"]];
NSError *error = nil;
if (![persistentStoreCoordinator addPersistentStoreWithType:NSSQLiteStoreType configuration:nil URL:storeUrl
options:options error:&error]){
NSLog(#"Unresolved error %#, %#", error, [error userInfo]);
}
return persistentStoreCoordinator;
}
- (NSManagedObjectModel *) managedObjectModel {
if (managedObjectModel != nil) {
return managedObjectModel;
}
managedObjectModel = [NSManagedObjectModel mergedModelFromBundles:nil];
return managedObjectModel;
}

Managing multiple NSPersistentStores with PersistentStoreCoodinator

I am trying to get NSPersistentStoreCoordinator to manage the deletion and insertion of multiple persistent stores. So far I have managed to configure the PSC with two stores and I have been able to remove either store by specifying its index.
Like this…
NSPersistentStore *store = [[self.persistentStoreCoordinator persistentStores] objectAtIndex:0];
if (![self.persistentStoreCoordinator removePersistentStore:store error:&error]) {
NSLog(#"Unresolved error %#, %#", error, [error userInfo]);
abort();
}
[[NSFileManager defaultManager] removeItemAtURL:store.URL error:&error];
But I'm finding that when I add the store back in to the PSC the index value is incorrect and it cannot be specified with the existing class methods. The consequence of this is that the new data is downloaded and added to the wrong store.
Does anyone have any suggestions on how to this should be done?
Background (Updated)
The reason for using two persistent stores is so that I can designate a unique store for the two xml documents that will be downloaded over the network. As both these files are relatively large I am hoping to reduce network traffic. So a check is done to see if either file has been modified. If they have then the corresponding persistent store is deleted and a new store added. It's at this point the problem starts. Adding a new store always adds it to the end of the Persistent Stores Array. This appears to create a mismatch in the stores when merging the data back with the MOC.
Code
Here's what I've tried so far which removes and adds the persistent store but the new data is added to the wrong store.
static NSString * const kLastDateStoreUpdateKey = #"eventLastStoreUpdateKey";
- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response {
NSString *last_modified = [NSString stringWithFormat:#"%#",[[(NSHTTPURLResponse *)response allHeaderFields] objectForKey:#"Last-Modified"]];
NSDateFormatter *dFormatter = [[NSDateFormatter alloc] init];
[dFormatter setDateFormat:#"EEE, dd MMM yyyy HH:mm:ss zzz"];
[dFormatter setLocale:[[NSLocale alloc] initWithLocaleIdentifier:#"en_GB"]];
[dFormatter setTimeZone:[NSTimeZone timeZoneWithAbbreviation:#"GMT"]];
dateModified = [dFormatter dateFromString:last_modified];
NSDate *previousDate = [[NSUserDefaults standardUserDefaults] objectForKey:kLastDateStoreUpdateKey];
if (!previousDate || [previousDate compare:dateModified] != NSOrderedSame) {
[self.managedObjectContext lock];
[self.managedObjectContext reset];
if ([[NSFileManager defaultManager] fileExistsAtPath:self.persistentStorePath]) {
NSError *error = nil;
NSArray *stores = [self.persistentStoreCoordinator persistentStores];
NSURL *storeUrls = [NSURL fileURLWithPath:persistentStorePath];
for (NSPersistentStore *store in stores){
if ([[store.URL absoluteURL] isEqual:[storeUrls absoluteURL]]) {
if (![self.persistentStoreCoordinator removePersistentStore:store error:&error]) {
NSLog(#"Unresolved error %#, %#", error, [error userInfo]);
abort();
}
[[NSFileManager defaultManager] removeItemAtURL:store.URL error:&error];
NSLog(#"Check store removed %#", [self.persistentStoreCoordinator persistentStores]);
}
}
}
NSURL *storeUrl = [NSURL fileURLWithPath:persistentStorePath];
NSError *error = nil;
[self.persistentStoreCoordinator addPersistentStoreWithType:NSSQLiteStoreType configuration:nil URL:storeUrl options:nil error:&error];
if (error) {
NSLog(#"event error %# %#",error, [[self.persistentStoreCoordinator persistentStores] objectAtIndex:0]);
}
NSLog(#"Check store added %#",self.persistentStoreCoordinator.persistentStores);
[self.managedObjectContext unlock];
}else {
[self cancelDownload];
NSLog(#"event cancel %# %# %#",previousDate, dateModified, [self.persistentStoreCoordinator persistentStores]);
}
}
Solved
As Hunter pointed out below my PSC Configuration was set to as nil. So data was not being added to each individual Persistent Store. When I created a Configuration in the Core Data Model and targeted the relevant Entities I set the PSC to have that configuration. It then worked as expected. See below.
NSURL *storeUrl = [NSURL fileURLWithPath:persistentStorePath];
NSError *error = nil;
[self.persistentStoreCoordinator addPersistentStoreWithType:NSSQLiteStoreType configuration:#"ConfigEvent" URL:storeUrl options:nil error:&error];
if (error) {
NSLog(#"event error %# %#",error, [[self.persistentStoreCoordinator persistentStores] objectAtIndex:0]);
}
NSLog(#"Check store added %#",self.persistentStoreCoordinator.persistentStores);
If you truly require the two stores [I can't tell from your explanation if that's really the best way to do this], you might want to reference them via properties or an ivar instead of relying on the array.
Alternatively, you could iterate through the PSC's stores array and identify each one by inspecting the URL.
Update:
If you're having trouble storing specific data in different NSPersistentStores, take a look at Core Data configurations. This allows you to tell Core Data to put specific entities in specific persistent stores.
You specify configurations in your model and when you add persistent stores. More here: https://developer.apple.com/library/mac/#documentation/Cocoa/Conceptual/CoreData/Articles/cdMOM.html
You should be able to achieve this with one Persistent Store Coordinator and two Managed Object Contexts. You only need to go to the complexity of two Persistent Store Coordinators if you are experiencing locking which is hurting performance.
Remember: The Simplest Possible Thing (tm)

Cannot fetch data from Core Data sqlite DB

First i want to apologize for starting more than one thread about this problem but i have taken step-by-step approach and steps to solve this very irritating problem. I have spend four days now to try to solve this.
The situation: I have an application where i use a pre-populated sqlite database (106k), via Core Data. I have moved the sqlite DB to the Resources folder and being able to copy it into documents. When i test i can see that the file exist, see code example and NSLog below.
I have used example code in the delegate, see below.
Everything works perfectly in the simulator but not when i test on the device.
Here is the code from the delegate:
- (NSPersistentStoreCoordinator *)persistentStoreCoordinator {
if (persistentStoreCoordinator_ != nil) {
return persistentStoreCoordinator_;
}
NSString *storePath = [[self applicationDocumentsDirectory] stringByAppendingPathComponent: #"FamQuiz_R0_1.sqlite"];
/*
Set up the store.
For the sake of illustration, provide a pre-populated default store.
*/
NSFileManager *fileManager = [NSFileManager defaultManager];
// If the expected store doesn't exist, copy the default store.
if (![fileManager fileExistsAtPath:storePath]) {
NSString *defaultStorePath = [[NSBundle mainBundle] pathForResource:#"FamQuiz_R0_1" ofType:#"sqlite"];
if (defaultStorePath) {
[fileManager copyItemAtPath:defaultStorePath toPath:storePath error:NULL];
}
}
NSURL *storeUrl = [NSURL fileURLWithPath:storePath];
NSDictionary *options = [NSDictionary dictionaryWithObjectsAndKeys:[NSNumber numberWithBool:YES], NSMigratePersistentStoresAutomaticallyOption, [NSNumber numberWithBool:YES], NSInferMappingModelAutomaticallyOption, nil];
persistentStoreCoordinator_ = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel: [self managedObjectModel]];
NSError *error;
if (![persistentStoreCoordinator_ addPersistentStoreWithType:NSSQLiteStoreType configuration:nil URL:storeUrl options:options error:&error]) {
// Update to handle the error appropriately.
NSLog(#"Unresolved error %#, %#", error, [error userInfo]);
exit(-1); // Fail
}
return persistentStoreCoordinator_;
}
Here is the code when i read the database:
- (void)createQuestionsList: (NSMutableArray *)diffArray {
NSLog(#">>createQuestionsList<<");
NSFileManager *filemgr = [NSFileManager defaultManager];
NSString *docsDirectory = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) objectAtIndex:0];
NSString *filePath = [docsDirectory stringByAppendingPathComponent:#"FamQuiz_R0_1.sqlite"];
if ([filemgr fileExistsAtPath: filePath ] == YES)
NSLog (#"\n\n\nFile exists in createQuestionList\n\n\n");
else
NSLog (#"\n\n\nFile not foundin createQuestionList\n\n\n");
int nrOfQuestions = [[diffArray objectAtIndex:0]intValue];
int nrOfEasyPlayers = [[diffArray objectAtIndex:1]intValue];
int nrOfMediumPlayers = [[diffArray objectAtIndex:2]intValue];
int nrOfHardPlayers = [[diffArray objectAtIndex:3]intValue];
int nrOfPlayers = nrOfEasyPlayers + nrOfMediumPlayers + nrOfHardPlayers;
allHardArrayQ = [[NSMutableArray alloc]init];
allMediumArrayQ = [[NSMutableArray alloc]init];
allEasyArrayQ = [[NSMutableArray alloc]init];
allDummyQ = [[NSMutableArray alloc]init];
allJointQ = [[NSMutableArray alloc]init];
hardQ = [[NSMutableArray alloc]init];
mediumQ = [[NSMutableArray alloc]init];
easyQ = [[NSMutableArray alloc]init];
masterQuestionsArray = [[NSMutableArray alloc] initWithObjects:
[NSNumber numberWithBool:[[diffArray objectAtIndex:4]boolValue]],
[NSNumber numberWithInt:nrOfHardPlayers],
[NSNumber numberWithInt:nrOfMediumPlayers],
[NSNumber numberWithInt:nrOfEasyPlayers],
[NSNumber numberWithInt:nrOfQuestions],
nil];
NSLog(#"masterQuestionsArray %#", masterQuestionsArray);
NSError *error;
//=========PREPARE CORE DATA DB===========//
if (managedObjectContext == nil) { managedObjectContext = [(FamQuiz_R0_1AppDelegate *)
[[UIApplication sharedApplication] delegate] managedObjectContext]; }
// Define qContext
NSManagedObjectContext *qContext = [self managedObjectContext];
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription
entityForName:#"questions" inManagedObjectContext:qContext];
[fetchRequest setEntity:entity];
NSArray *fetchedObjects = [qContext executeFetchRequest:fetchRequest error:&error];
for (NSManagedObject *info in fetchedObjects) {
if ([[info valueForKey:#"qDiff"] intValue] == 1) {
[allEasyArrayQ addObject:[info valueForKey:#"idQ"]];
} else if ([[info valueForKey:#"qDiff"] intValue] == 2) {
[allMediumArrayQ addObject:[info valueForKey:#"idQ"]];
} else if ([[info valueForKey:#"qDiff"] intValue] == 3) {
[allHardArrayQ addObject:[info valueForKey:#"idQ"]];
}
}
NSLog(#"allEasyArrayQ %#", allEasyArrayQ);
NSLog(#"allMediumArrayQ %#", allMediumArrayQ);
NSLog(#"allHardArrayQ %#", allHardArrayQ);
Here is the output:
2011-04-25 19:35:45.008 FamQuiz_R0_1[963:307] >>createQuestionsList<<
2011-04-25 19:35:45.021 FamQuiz_R0_1[963:307]
File exists in createQuestionList
2011-04-25 19:35:45.031 FamQuiz_R0_1[963:307] masterQuestionsArray (
1,
0,
0,
1,
5
)
2011-04-25 19:35:45.238 FamQuiz_R0_1[963:307] allEasyArrayQ (
)
2011-04-25 19:35:45.246 FamQuiz_R0_1[963:307] allMediumArrayQ (
)
2011-04-25 19:35:45.254 FamQuiz_R0_1[963:307] allHardArrayQ (
)
2011-04-25 19:35:45.311 FamQuiz_R0_1[963:307] *** Terminating app due to uncaught exception 'NSRangeException', reason: '*** -[NSMutableArray objectAtIndex:]: index 0 beyond bounds for empty array'
I REALLY appreciate all help i can get to solve this extremely frustrating problem :-)
UPDATE
I did put in a NSLog in the delegate to check the storeURL and got the following result when running on the external iPhone device:
storeURL: file://localhost/var/mobile/Applications/7F5FDB03-0D22-46BC-91BC-4D268EB4BBEB/Documents/FamQuiz_R0_1.sqlite
The following is when i run in the simulator, without the external iPhone device:
storeURL: file://localhost/Users/PeterK/Library/Application%20Support/iPhone%20Simulator/4.2/Applications/E27EC1A4-3A87-4A1B-97DE-D464679005BE/Documents/FamQuiz_R0_1.sqlite
The crash you are getting is not coming from the posted code. The last line in the code is:
NSLog(#"allHardArrayQ %#", allHardArrayQ);
... which prints.
Somewhere down the line from the provided code you make a call like this:
[someArray objectAtIndex:0]
... while someArray is empty and that causes the out of range error. Most likely, someArray is one of the three empty arrays just logged but not necessarily.
Your arrays are showing empty because (1) you are getting no returns from your fetch or (2) the values for qDiff and idQ aren't what or where you think they are. To confirm, add logs:
NSArray *fetchedObjects = [qContext executeFetchRequest:fetchRequest error:&error];
NSLog(#"fetchedObjects =",fetchedObjects);
for (NSManagedObject *info in fetchedObjects) {
NSLog(#"[info valueForKey:#"qDiff"]",[info valueForKey:#"qDiff"]);
NSLog(#"[info valueForKey:#"idQ"]",[info valueForKey:#"idQ"]]);
if ([[info valueForKey:#"qDiff"] intValue] == 1) {
[allEasyArrayQ addObject:[info valueForKey:#"idQ"]];
....
... that well tell you if you are getting the data in the first place. If not then you need to look at the configuration of your Core Data stack.
More generally, I think your code demonstrates that you haven't quite conceptually grasped Core Data yet. Instead, you seem to being trying to treat Core Data as a lightweight object wrapper around SQL. This is a common misperception among those skilled at SQL but new to Core Data and it causes a lot of design issues.
Firstly, there is no reason to check for the physical presence of the store file after you have initialize the persistent store. The NSPersistentStore object in memory cannot exist without the store file so if the object is there, the file is always there.
Secondly, you are piling on a lot of additional data structures in the form of arrays. This is a common need when dealing with raw SQL but is almost always unnecessary when using Core Data. The entire point of Core Data is to manage data manually (persisting it to the disk is just an option and is not required.) All your arrays should most likely be entities in the data model and you should be using fetches and sorts to pull out specific pieces of data ordered for the immediate needs of any particular view.
It's very common to see people with a strong SQL or similar background doing way, way more work than needed to implement Core Data. It's like someone who has drive a manual transmission for year who switches to an automatic. They keep stepping on the non-existent clutch and fiddling with the gear shift.
Seems like you are not getting anything from the context, all three arrays are printed empty. And the exception seems to be thrown from out of what you ve pasted here. Frankly, there are irrelevant, redundant parts like;
//=========PREPARE CORE DATA DB===========//
if (managedObjectContext == nil) { managedObjectContext = [(FamQuiz_R0_1AppDelegate *)
...which is never used, but the main problem is i guess, you are trying to access an empty array assuming something from the context comes in there, but apparently not...
You should try it step by step and be sure that your context operations return anything...
And clean the code a bit so that you can see the problem easier, or anyone who is trying to help you...
PROBLEM SOLVED
After spending yet another evening trying to debug every line in the code, without understanding, i downloaded "iPhone Explorer" and manually deleted the two instances of the DB directly on the device and after that it finally worked.
I noticed that the DB was not copied to the documents folder, there an empty DB resided that was smaller that the populated DB.
Now both DB instances is the correct ones :-) ...meaning that copy works now.
If you want the "iPhone Explorer" you will find it here: http://www.macroplant.com/iphoneexplorer/

CoreData Save Permanently?

I have been working with Core Data in an iPad app and I can successfully save and fetch data inside the app. However when completely closing the application, fully, quit, take it out of multitasking, and that data disappears.
So does Core Data in anyway keep this data anywhere when the app is closed? Or do I need to look somewhere else?
EDIT: This is in the app delegate didFinishLaunchingWithOptions: [[[UIApplication sharedApplication] delegate] managedObjectContext]; and then I have this: context_ = [(prototypeAppDelegate *)[[UIApplication sharedApplication] delegate] managedObjectContext]; in the UIView subclass.
This is the NSPersistentStoreCoordinator code premade in the app delegate:
- (NSPersistentStoreCoordinator *)persistentStoreCoordinator {
if (persistentStoreCoordinator_ != nil) {
return persistentStoreCoordinator_;
}
NSURL *storeURL = [[self applicationDocumentsDirectory] URLByAppendingPathComponent:#"prototype.sqlite"];
NSError *error = nil;
persistentStoreCoordinator_ = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel:[self managedObjectModel]];
if (![persistentStoreCoordinator_ addPersistentStoreWithType:NSSQLiteStoreType configuration:nil URL:storeURL options:nil error:&error]) {
/*
Replace this implementation with code to handle the error appropriately.
abort() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development. If it is not possible to recover from the error, display an alert panel that instructs the user to quit the application by pressing the Home button.
Typical reasons for an error here include:
* The persistent store is not accessible;
* The schema for the persistent store is incompatible with current managed object model.
Check the error message to determine what the actual problem was.
If the persistent store is not accessible, there is typically something wrong with the file path. Often, a file URL is pointing into the application's resources directory instead of a writeable directory.
If you encounter schema incompatibility errors during development, you can reduce their frequency by:
* Simply deleting the existing store:
[[NSFileManager defaultManager] removeItemAtURL:storeURL error:nil]
* Performing automatic lightweight migration by passing the following dictionary as the options parameter:
[NSDictionary dictionaryWithObjectsAndKeys:[NSNumber numberWithBool:YES],NSMigratePersistentStoresAutomaticallyOption, [NSNumber numberWithBool:YES], NSInferMappingModelAutomaticallyOption, nil];
Lightweight migration will only work for a limited set of schema changes; consult "Core Data Model Versioning and Data Migration Programming Guide" for details.
*/
NSLog(#"Unresolved error %#, %#", error, [error userInfo]);
abort();
}
return persistentStoreCoordinator_;
}
So far I am using this to fetch data:
NSFetchRequest *fetch = [[NSFetchRequest alloc] init];
NSEntityDescription *testEntity = [NSEntityDescription entityForName:#"DatedText" inManagedObjectContext:context_];
[fetch setEntity:testEntity];
NSPredicate *pred = [NSPredicate predicateWithFormat:#"dateSaved == %#", datePicker.date];
[fetch setPredicate:pred];
NSError *fetchError = nil;
NSArray *fetchedObjs = [context_ executeFetchRequest:fetch error:&fetchError];
if (fetchError != nil) {
NSLog(#"fetchError = %#, details = %#",fetchError,fetchError.userInfo);
}
noteTextView.text = [[fetchedObjs objectAtIndex:0] valueForKey:#"savedText"];
And this to save data:
NSManagedObject *newDatedText;
newDatedText = [NSEntityDescription insertNewObjectForEntityForName:#"DatedText" inManagedObjectContext:context_];
[newDatedText setValue:noteTextView.text forKey:#"savedText"];
[newDatedText setValue:datePicker.date forKey:#"dateSaved"];
NSError *saveError = nil;
[context_ save:&saveError];
if (saveError != nil) {
NSLog(#"[%# saveContext] Error saving context: Error = %#, details = %#",[self class], saveError,saveError.userInfo);
}
Do you save the context in the right places? It is a common mistake not to save the context when entering background application state only in willTerminate.
Save the context in the following appdelegate method:
-(void)applicationDidEnterBackground:(UIApplication *)application
You are saving your context directly after inserting the object, this should be sufficient. Check the sqlite file in simulator if it contains any data after saving.
if
noteTextView.text = [[fetchedObjs objectAtIndex:0] valueForKey:#"savedText"];
does not throw an exception, there is an object found in context. Maybe it does not contain the expected value?
Log the returned object from your fetchrequest to console to see if this might be the case
You should post the code that sets up your NSPersistentStoreCoordinator and adds your NSPersistentStore. By any chance are you using NSInMemoryStoreType as the type of your store? Because that would result in the behavior you're seeing. Alternately, you could be using a different path to the store each time, which would give you a fresh store each time. In general, your store should be in your Documents folder, and it should be given the same name on every launch. It should also use the NSSQLiteStoreType
I have discovered the problem. It turns out that due to its use of UIDatePicker, at the start of the program it set that date picker to today using:
NSDate *now = [[NSDate alloc] init];
[datePicker setDate:now];
So without using this it works perfectly. So currently I am looking for a solution to this issue, as this line seems to cause the problem.
UIDatePicker Interfering with CoreData
If you add a CoreData after create your project.There is a risk to make mistake in lazy_init your NSManagedObject.
-(NSManagedObjectContext*) managedObjectContext{
if (!_managedObjectContext) {
_managedObjectContext =[self createManageObjectContextWithName:#"name.sqlite"];
}
return _managedObjectContext;}
Here is the right way:
- (NSManagedObjectContext *)managedObjectContext {
if (_managedObjectContext != nil) {
return _managedObjectContext;
}
NSPersistentStoreCoordinator *coordinator = [self persistentStoreCoordinator];
if (!coordinator) {
return nil;
}
_managedObjectContext = [[NSManagedObjectContext alloc] init];
[_managedObjectContext setPersistentStoreCoordinator:coordinator];
return _managedObjectContext;}

Core Data Lightweight Migration - Cant Merge Models

I need to add some attributes to my core data model and I am having a terrible time getting lightweight migration to work!
I keep getting the error "Cant merge models with two different entities named blah".
Here's what I've done ...
Added this code to my app delegate.
(NSPersistentStoreCoordinator*)persistentStoreCoordinator {
//blah blah
NSDictionary* options =
[NSDictionary dictionaryWithObjectsAndKeys:
[NSNumber numberWithBool:YES], NSMigratePersistentStoresAutomaticallyOption,
[NSNumber numberWithBool:YES], NSInferMappingModelAutomaticallyOption, nil];
//blah blah
return _persistentStoreCoordinator;
}
Clicked on my data model, went to Design > Data Model > Add Model Version.
Made my changes to the one with the LOWEST number, basically adding a few attributes.
Deleted all the managed files produced from my previous model, sent them to trash, then created new ones from the new model.
Cleaned all targets.
Build and go.
ERROR.
Please please help. I've tried the above in numerous different ways, and loads of other stuff, each time going back to a clean copy of my project and starting again, and nothing has got me past this error.
Thanks!
Well, once again, another 6 hours of my life completely wasted because Apple are a bunch of ... well, I'll stop there.
Anyway, thanks to this lovely person: http://linkroller.com/fullpage/ad/13754/?http://iphonedevelopment.blogspot.com/2009/09/core-data-migration-problems.html I was able to solve the problem.
You follow the steps I already followed, then you need to find the following method:
- (NSManagedObjectModel *)managedObjectModel {
if (managedObjectModel != nil) {
return managedObjectModel;
}
managedObjectModel = [[NSManagedObjectModel mergedModelFromBundles:nil] retain];
return managedObjectModel;
}
and change it to:
- (NSManagedObjectModel *)managedObjectModel {
if (managedObjectModel != nil) {
return managedObjectModel;
}
NSString *path = [[NSBundle mainBundle] pathForResource:#"Foo" ofType:#"momd"];
NSURL *momURL = [NSURL fileURLWithPath:path];
managedObjectModel = [[NSManagedObjectModel alloc] initWithContentsOfURL:momURL];
return managedObjectModel;
}
where foo is the name of you xcdatamodeld file.
AAAAAARGH.
I had fixed the core data migration
pls follwing this steps
Go AppDelegate.m write function
-(NSPersistentStoreCoordinator *)persistentStoreCoordinator
{
if (__persistentStoreCoordinator != nil)
{
return __persistentStoreCoordinator;
}
NSString *databaseFilePath = [[self applicationDocumentsDirectory] stringByAppendingPathComponent: #"sampleiOS.sqlite"];
NSURL *storeUrl = [NSURL fileURLWithPath: databaseFilePath];
NSDictionary *_option = [NSDictionary dictionaryWithObjectsAndKeys:
[NSNumber numberWithBool:YES], NSMigratePersistentStoresAutomaticallyOption,
[NSNumber numberWithBool:YES], NSInferMappingModelAutomaticallyOption, nil];
NSError *error = nil;
__persistentStoreCoordinator = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel:[self managedObjectModel]];
if (![__persistentStoreCoordinator addPersistentStoreWithType:NSSQLiteStoreType configuration:nil URL:storeUrl options:_option error:&error]) {
NSLog(#"Unresolved error %#, %#", error, [error userInfo]);
}
return __persistentStoreCoordinator;
}
then select example.xcdatamodeld file
click the Editor menu on the top => add model version => create the new model version "example 2.xcdatamodel" then click finish button.
now show two core data model version one is source "example.xcdatamodel" another one is destination "example 2.xcdatamodel".
now add an attribute or entity in your new version datamodel "example 2.xcdatamodel". then click the group data model "example.xcdatamodeld". After that set current version to be newly created data model "example 2.xcdatamodel".
How to set current Version
select show utilities => show inspector => versioned core data model . then set current version.