CoreData lightweight migration failing on iOS - iphone

I'm trying to migrate my core data data to a new version. The only change I've done is adding one field (NSString *sessionId)
So what I've done:
1. Create version 2 of my core data file
2. Add sessionId in this file.
3. set version 2 as default.
4. add sessionId in Offer.m and .h
5. When initializing the persistentStoreCoordinator adding the options:
NSDictionary *options = [NSDictionary dictionaryWithObjectsAndKeys:
[NSNumber numberWithBool:YES], NSMigratePersistentStoresAutomaticallyOption,
[NSNumber numberWithBool:YES], NSInferMappingModelAutomaticallyOption, nil];
When Im starting my application when i thought it would get the updated model i get:
Unresolved error Error
Domain=NSCocoaErrorDomain Code=134130
"The operation couldn’t be completed.
(Cocoa error 134130.)"
UserInfo=0x7643200
{URL=/Users/ei/Library/Application
Support/iPhone
Simulator/4.0.2/Applications/81B816AE-5CA0-46DB-83E7-6A765EBF9D05/Documents/Offers.sqlite, metadata={type = immutable dict,
count = 7,
Followed by
reason=Can't find model for source store
I get the same error if I don't set the options when loading the persistentStoreCoordinator
What is it I am missing?

Apparently, if you merge two models and then try to automatically migrate from one model to another doesnt work. We ended up separating them.

The error message means exactly what it says: it's missing the .xcdatamodel (or strictly .mom) that the database was saved with. I'd try grabbing the .xcdatamodel from an old revision and seeing if there have been accidental changes.
Another problem is that Xcode isn't good at deleting files which are no longer being built, both when building and when installing (Xcode is allowed to do "incremental installs" to make development quicker, and at least in previous versions, never deleted files). Instead of building MyApp.app/MyModel.mom, it's now building MyApp.app/MyModel.momd/MyModel.mom, but the old MyModel.mom might stick around and confuse whatever you're using to load the model (usually it complains about being unable to merge models, though...). The fix is to nuke the build directory and install the app using OTA/iPhone Configuration Utility/iTunes (in order of user-friendliness), and is incidentally why I tell Xcode 4 to stick build products in the project dir (the reason why they changed it is presumably because people aren't smart enough to not commit it...).

Related

XML generation and parsing from Core data in iphone

I'm creating a simple iOS application consisting of a few UITableViewControllers. The information displayed in the view controllers will come from a xml file (that I'll include in the project's Resources or direct from dropbox or iCloud). The xml file's contents will be based on user input .
A few notes:
The data is based on the user input means not static. Ideally the app will load the data into "Core Data" from xml file.
Each additional run of the app will just pull data from some Core Data source (that I'm not completely familiar w/ yet) instead of re-loading it from the textfile.
right now I am using XMLwriter to generate simple xml file
Please guide me
thank you
The best pattern here seems to be to use the XML file to "seed" your Core Data database. This only happens the first time. After that you will never again use your XML file but simply update and sync your core data store.
This is far better than generating XML. The problem with XML files (like property lists) is that you have to write the entire file for each little incremental change. If you sync to a store somewhere online, this can take much too much time to be practical.
Assuming you can get a foundation object from the XML file, simply iterate through the object and insert a Core Data one by one.
for (NSDictionary *dict in xmlArray) {
Entity *newObject = [NSEntityDescription
insertNewObjectForEntityForName:#"Entity"
inManagedObjectContext:self.managedObjectContext];
newObject.attribute1 = [dict objectForKey:#"attribute1"];
newObject.attribute2 = [dict objectForKey:#"attribute2"];
// etc...
}
[self.managedObjectContext save:nil];

Adding an Attribute to a Core Data Entity

this has beed discussed alot, but nothing that I found helped me... I just want to add a single attribute to a singe entity. Of cause without loosing my data.
To do that, I followed these steps
But I don't think it worked at all. There are no errors as long as not try to use the new attribute. But in my Core Data Model (when I open the .xcdatamodel in XCode) it's there and even in my NSManagedObject Subclass of the entity, the new attribute is there as a property. But when I try to use it, this happens:
unrecognized selector sent to instance 0x74f8520
or
the entity Name is not key value coding-compliant for the key "isFavoriteName".'
The key "isFavoriteName"is the one I just added.Like I said, I did all the steps. Do I miss something?
I think, that the new Model Version is not being used, because when I delete some attributes in the Model, my app still works and the deleted information are still shown... I have set the current Core Data Model Version the the new one (Step 6).
I hope you can help me!
Nick
EDIT: Do I may have to change something in code as well? Apart from the setting the options when creating persistentStoreCoordinator... Because I have, let's say myApp.xdatamodel and inside of that, i know have myApp.xdatamodel and myApp 2,xdatamodel... but I my code I sill do:
NSURL *modelURL = [[NSBundle mainBundle] URLForResource:#"myApp" withExtension:#"mom"];
_managedObjectModel = [[NSManagedObjectModel alloc] initWithContentsOfURL:modelURL];
That's right-- the page you linked to left out one step that applies in your case. Even though you created a new version of the model, your code is still loading the old one by name.
Don't just change it to load version 2 though. Do this instead:
NSURL *modelURL = [[NSBundle mainBundle] URLForResource:#"myApp" withExtension:#"momd"];
This will look in the momd folder and get whatever the current version is. In this case it will get version 2. If you change the model again in the future, it will get whatever version is current then.

sqlite prepare statement error - no such table

I'm having some difficulty with my sqlite prepare statement. I get an error saying my table does not exist, although I've checked in multiple places for it, and it does exist, so I'm confuzzled.
The file is in the correct iPhone Simulator Application folder
The file is added to my project and viewable in the project navigator
It is also in my build phases- Copy Bundle Resources area.
I've cleaned and started running again.
The database exists and running my sql statement gets me just the
results I expected.
- (NSMutableArray *) getMyWorkout{
NSMutableArray *workoutArray = [[NSMutableArray alloc] init];
#try {
NSFileManager *fileMgr = [NSFileManager defaultManager];
NSString *dbPath = [[[NSBundle mainBundle] resourcePath ]stringByAppendingPathComponent:#"IOSDB.sqlite"];
NSLog(#"Db path is %#",dbPath);
BOOL success = [fileMgr fileExistsAtPath:dbPath];
if(!success) {
NSLog(#"Cannot locate database file '%#'.", dbPath);
}
if(!(sqlite3_open([dbPath UTF8String], &db) == SQLITE_OK)){
sqlite3_close(db);
NSLog(#"Failed to open database with message '%s'.", sqlite3_errmsg(db));
}
const char *sql = "SELECT Id, type, difficulty, duration, description FROM workoutTbl";
sqlite3_stmt *sqlStatement;
if(sqlite3_prepare(db, sql, -1, &sqlStatement, NULL) != SQLITE_OK){
NSLog(#"%s Prepare failure '%s' (%1d)", __FUNCTION__, sqlite3_errmsg(db), sqlite3_errcode(db));
} //...
When I run it, I get the file path and the following error
2013-02-01 18:07:08.060 TriShake[9251:c07] -[MyWorkoutList getMyWorkout] Prepare failure 'no such table: workoutTbl' (1)
I've checked out these other questions, but have been unable to find a solution
Sqlite Prepare Failed: no such table<tablename>
Sqlite3 gives "no such table" error on iPhone
I understand sqlite3_open() creates an empty database for you if the database path does not exist, but i know it exists, so frustration ensues. Any help or guidance you could give me would be much appreciated.
In terms of your immediate problem, it's going to be something simple.
You say you've "cleaned and built again", but have you actually removed the old app from your simulator? Either remove the app manually, or, easier, just reset the simulator entirely by choosing "reset content and settings" from the "iOS Simulator" menu. Sometimes Xcode is not good about knowing what files to copy over (particularly in a case like this where your running it on the device may be changing the timestamp of the file in the simulator's bundle!)
Run the app again.
If the app doesn't work as expected, open up the database in the simulator folder from the Mac and check out the database to make sure the table is there and precisely as you expected it to be. So navigate to the app, open the bundle (you may have to choose the "show package contents" option), confirm the existence of the database, but just as importantly, open it up this particular copy of the database in your favorite Mac sqlite3 tool of choice and confirm the existence of the table there.
Let us know what you find. Again, it's got to be something simple such as:
Perhaps the process of rebuilding the app was not reinstalling everything; I've occasionally had problems where Xcode elected to not re-copy something during the install on my simulator;
Perhaps your database in your project was accidentally put in a subdirectory, worse, you might have two copies sitting in different directories;
Perhaps the database in your Xcode project is missing (or has a typo or (esp in the case of the device) has incorrect filename capitalization) in the name of the table or file;
Etc.
For a lot of these errors, you won't notice the problem until you completely reset the simulator itself. There are a million little things it could be, but hopefully completely resetting the simulator and starting over will help you find the issue. It's always something simple when it comes to these sorts of issues.
Some other minor observations:
You probably should not be opening databases from the bundle. Programmatically copy it from the bundle to the Documents folder, and open the database from there. I know it seems unnecessary, but it's important for a myriad of reasons (if db changes during operation of the app, if db accidentally gets created on you, don't let Xcode get confused about things that changed (even if only file timestamps) in the bundle changing behind Xcode's back, etc.)
You should, if you need the database to be there, use sqlite3_open_v2, using either SQLITE_OPEN_READWRITE or SQLITE_OPEN_READONLY for flags (but do not include SQLITE_OPEN_CREATE). It causes headaches to ever give sqlite a chance to create a blank database for you, or otherwise modify it, so never give it an opportunity to so.
I have encounter the same problem as yours. If the IOS can not find the designated database file, defaultly it will create one for you instead of throwing an error. So you must open the database file IOS created for you which is blank so it off course contain the table you expected.
what I deal with it :
1 you have to bundle the resource file named *.sqlite3 into your project
2 Then You have to use [NSBundle mainBundle] pathFordirectory...... function to search your proper database file.
then you can open the database file you expected and can operate it properly
Best regards,
Not enough rep to comment on Jack's post, but that helped me.
In my case, I had mistyped my path for resource extension:
// Wrong
NSString *sqLiteDb = [[NSBundle mainBundle] pathForResource:#"productList"
ofType:#"sqlite3"];
// Should have been (added db ext)
NSString *sqLiteDb = [[NSBundle mainBundle] pathForResource:#"productList"
ofType:#"db"];
I would always get past the:
if (sqlite3_open([sqLiteDb UTF8String], &_database) == SQLITE_OK))
because it was automatically creating a db file for me.

How to remove data before update

I have newer version app to update to app store, but since there were some obsolete data: such as database and some files that I don't need in the newer build or some databases which are use same name as newer build but the DB structure is different, so is there a way to remove all personal data of last version when user do the update? Then after user updated my app the app feels like a brand-new one.
Deleting the entire contents of your application's sandbox in one fell swoop is really something that only can be accomplished by the user removing and re-installing your application.
There are two things to consider here:
If you know where the database/files reside, you can use the NSFileManager methods to remove them explicitly. Especially removeItemAtPath:error:. The Class Reference is quite helpful.
You should proceed with caution when clearing out the entire application's state. Do your users know this is going to happen? Are they going to lose sensitive data?
There is a pattern that you can follow.
Let's say you db is called MySqliteDatabase.sqlite. There is some other data in Documents directory but you dont want to use it and delete it on first install of next version.
With the new version, rename the db to MySqliteDatabasev2.sqlite. On appstartup, use NSFileManager to look for MySqliteDatabasev2.sqlite in the documents directory. If it doesn't exist, blow away the documents directory and copy My SqliteDatabasev2.sqlite there.
For any subsequent launches, the above logic will find the v2 data and will do nothing with the documents directory.
For any subsequent version upgrades, you can follow this scheme to update database name.
Have caution: make sure to change rest of the code to use the new name as well. Best practice for that is to define it in some commons header file (i have a commons.h in every project)
#define DB_NAME #"MySqliteDbv2.sqlite"
and then make sure everywhere you are just using DB_NAME and nothing anything else. So whenever version changes:
1. rename the db file in xcode navigator.
2. rename the DB_NAME define.
That's it.
On app start up you can simply remove the entire store file with something like this.
NSString *path = <path to your file>
[[NSFileManager defaultManager] removeItemAtPath:[self persistentStorePath] error:nil];
You can use this code to get the path to the documents directory, if that is where you store the data.
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *path = [[[paths lastObject] stringByAppendingPathComponent:#"subdirectoryIfAny"]
stringByAppendingPathComponent:#"name of your .sql or whatever"];
If you do not want to blow away all the data, then you have read in the records you want to delete and then manually remove them.

Error when automatically migrating in core data

I am trying to migrate in core data using the automatic migration. The difference between the two versions is I added an additional attribute a model. When I attempt to add the PersistentStore to the coordinator I get the following exception
Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'Unrecognized column in entity'
If I remove the attribute the application loads fine (keeping the xcdatamodeld file set to the newer version or setting it to the earlier one.)
Any ideas on what could be causing this? Google turned up nothing.
What are your store options like? Mine are:
NSDictionary *options = [NSDictionary dictionaryWithObjectsAndKeys: [NSNumber numberWithBool:YES], NSMigratePersistentStoresAutomaticallyOption, [NSNumber numberWithBool:YES], NSInferMappingModelAutomaticallyOption, nil];
Also, make sure you are creating a new model version and adding your new attribute to that, and ensure that your previous model matches the current store. Are the only data models you have inside the xcdatamodeld file? And there are no others in your bundle?