I have built up a basic database application in which user can insert records onto a table and app displays them on a TableView.
Everything is working as it is supposed to be. For example, the new records do display even if we kill the app from app switcher and relaunch it from the springboard.
BUT every time I build and run using Xcode, the database just goes to default records! The new records are just not there.
Is it normal?... but what if I want to test my app for new records? Any fix?
BTW, JFYI, below is the code I use to make editable DB.
-(NSString *)createEditableDatabase{
// Check if DB already exists
BOOL success;
NSFileManager *fileManager = [NSFileManager defaultManager];
NSError *error;
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDir = [paths objectAtIndex:0];
NSString *writableDB = [documentsDir stringByAppendingPathComponent:#"Database.db"];
success = [fileManager fileExistsAtPath:writableDB];
//The editable DB already exists
if (success) {
return writableDB;
}
//The editable DB does not exist
//Copy the default DB into App's Doc Dir.
NSString *defaultPath = [[[NSBundle mainBundle] resourcePath] stringByAppendingPathComponent:#"Database.db"];
success = [fileManager copyItemAtPath:defaultPath toPath:writableDB error:&error];
if (!success) {
NSAssert1(0, #"Failed to create writable DB file: '%#'", [error localizedDescription]);
}
return writableDB;
}
While digging deeper, I noticed that the database modify date in finder was not updating when I inserted a record. So, I found out that I was still using old path to perform DB operations (not the Documents one). :) Now everything working fine. Anyways thanks Nick
Related
I am adding an App Group to my app for sharing a single plist between the app and the watch. I used to copy a plist from the bundle to Documents for when the app first started up. But with the watch I am now trying to convert it to save to the container but it always seems to be empty. The targets have app group enabled, and I am using the right name in my code. What could be going wrong?
Old Way
// COPY PLIST TO DOCUMENTS
NSFileManager *fileManger=[NSFileManager defaultManager];
NSError *error;
NSArray *pathsArray = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory,NSUserDomainMask,YES);
NSString *doumentDirectoryPath=[pathsArray objectAtIndex:0];
NSString *destinationPath= [doumentDirectoryPath stringByAppendingPathComponent:#"badger.com.vacations.plist"];
NSLog(#"plist path %#",destinationPath);
if (![fileManger fileExistsAtPath:destinationPath]){
NSString *sourcePath=[[[NSBundle mainBundle] resourcePath]stringByAppendingPathComponent:#"badger.com.vacations.plist"];
[fileManger copyItemAtPath:sourcePath toPath:destinationPath error:&error];
}
New way - not working
// COPY PLIST TO CONTAINER
NSURL *containerURL = [[NSFileManager defaultManager] containerURLForSecurityApplicationGroupIdentifier:#"group.com.xxxxx.xxx.container"];
containerURL = [containerURL URLByAppendingPathComponent:#"name.com.data.plist"];
NSString *destinationPath= containerURL.path;
NSLog(#"destinationPath %#", containerURL);
NSString *sourcePath=[[[NSBundle mainBundle] resourcePath]stringByAppendingPathComponent:#"name.com.data.plist"];
[fileManger copyItemAtPath:sourcePath toPath:containerURL.path error:&error];
You can´t share a plist as a file (or I simply don´t know about this feature), instead you just generate a new NSUserDefaults instance, which is shareable between targets.
Have a look here:
https://devforums.apple.com/message/977151#977151
or the Apple documentation
https://developer.apple.com/library/prerelease/ios/documentation/General/Conceptual/ExtensibilityPG/ExtensionScenarios.html
In the Apple Member Center, under 'Identifiers > App Groups' register a new App Group (e.g. group.de.myApp.sharedGroup)
In the Apple Member Center, under 'Identifiers > App IDs' refresh your App Ids for the targets that need sharing to use the App Groups feature
Regenerate all needed Provisioning Profiles and get them into Xcode
Back to Xcode: Under 'Capabilities' in each of your targets that need to share data, set 'App Groups' to on and add the previously registered App group.
Talk to the shareable NSUserDefaults container like this:
Store stuff:
NSUserDefaults *groupDefaults = [[NSUserDefaults alloc] initWithSuiteName:#"group.de.myApp.sharedGroup"];
[groupDefaults setInteger:1337 forKey:#"testEntry"];
[groupDefaults synchronize];
Read stuff:
NSUserDefaults *groupDefaults = [[NSUserDefaults alloc] initWithSuiteName:#"group.de.myApp.sharedGroup"];
NSInteger testEntry = [groupDefaults integerForKey:#"testEntry"];
NSLog(#"testEntry: %ld", (long)testEntry);
I'm getting an unrecognized selector on a newly added field to my Core Data (SQLite) db on iOS. I added a new model version as directed using Xcode's Editor menu, and then verified that new version is the current one. I made sure also that the .h and .m files for the modified table were updated, though I did this by hand (how to have these generated for you?). Nothing unusual though, just a String type field.
The problem seems to be that the lightweight migration never takes place by the time code is run that tries to reference the database object. Tring to access newFieldName gives:
-[MyEntity newFieldName]: unrecognized selector sent to instance 0x852b510
2014-03-27 13:26:21.734 ASSIST for iPad[41682:c07] *** Terminating app due to
uncaught exception 'NSInvalidArgumentException', reason: '-[MyEntity newFieldName]:
unrecognized selector sent to instance 0x852b510'
The line of code that generates the above error is the only line in the for loop below:
DataStoreCoreData *dStore = [[DataStoreCoreData alloc] initWithDataItemDescription:did];
for (MyEntity *myEnt in [dStore objects])
NSString *name = [myEnt newFieldName];
As mentioned, when I examine the SQLite db it has no new field in it, which makes sense given the error. So I also stepped through the execution of the code that is supposed to do the migration and it seems to work fine. Success. It looks like the following:
NSURL *storeUrl = [NSURL fileURLWithPath: [[self applicationDocumentsDirectory] stringByAppendingPathComponent: #"ASSIST.sqlite"]];
// handle db upgrade
NSDictionary *options = [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:options error:&error])
NSLog(#"\r\n Fail. [error localizedDescription]: %#", [error localizedDescription]);
else
NSLog(#"\r\n Success");
This is the 6th db version upgrade. All have been lightweight migrations with no unusual problems. Should not the above code force the SQLite db to reflect the new schema? How do I get it to do so, or is there another problem here?
I finally discovered the answer. The code I provided with the question is only part of the code required to be modified when making minimal changes to the database (lightweight migration). You also need to specify the new version when you create the object model. Notice "MyNewVersion" in the code below. You are supposed to update this parameter to reflect the new version you created and then selected as the current Model Version:
NSString *path = [[NSBundle mainBundle] pathForResource:#"MyNewVersion" ofType:#"mom" inDirectory:#"ASSIST.momd"];
NSURL *momURL = [NSURL fileURLWithPath:path];
managedObjectModel = [[NSManagedObjectModel alloc] initWithContentsOfURL:momURL];
I have one Tab Bat Controller and 2 UIView, in one view I insert or update the sqlite and the other view I read the sqlite data but when I access first time the view that read the sqlite the result is ok but when I insert or update the sqlite and then go to the other view the sqlite have the old results. I can see the new result only If I close the application and open again.
I want to know what I have to do to load the sqlite data every time that I access the results view.
For example: how I delete the sqlite memory to load again when I access the view with the new results from the database.
To get the results I do this:
-(NSMutableArray*)selectPossui{
cromoList = [[NSMutableArray alloc]init];
FMDatabase *database = [FMDatabase databaseWithPath:[[self dataFilePath] stringByAppendingPathComponent:#"bd.sqlite"]];
[database open];
FMResultSet *result = [database executeQuery:#"SELECT * FROM cromo where possui = 1"];
while ([result next]) {
programmer = [[Coder alloc] init];
programmer.codigo = [result stringForColumn:#"id"];
programmer.possui = [result stringForColumn:#"possui"];
programmer.repetida = [result stringForColumn:#"repetida"];
[cromoList addObject:programmer];
programmer=nil;
}
[database close];
return cromoList;
}
I had to search for many posts regarding these errors, but still I cannot fix the problem. Here is my code, can anyone help me to see what is going wrong?
- (void) copyDatabaseIfNeeded {
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
NSString *path = [documentsDirectory stringByAppendingPathComponent:#"SQL.sqlite"];
if (sqlite3_open([path UTF8String], &newDBconnection) == SQLITE_OK)
{
NSLog(#"Database opened successfully");
if(updateStmt == nil) {
NSString *updStmt = [NSString stringWithFormat: #"UPDATE Coffee SET CoffeeName = '500 Plus', Price = '1.40' Where CoffeeID= '3'"];
const char *mupdate_stmt = [updStmt UTF8String];
if(sqlite3_prepare_v2(newDBconnection, mupdate_stmt, -1, &updateStmt, NULL) != SQLITE_OK){
NSAssert1(0, #"Error while creating update statement. '%s'", sqlite3_errmsg(newDBconnection));
} else {
NSLog(#"Update successful");
}
}
if(SQLITE_DONE != sqlite3_step(updateStmt))
NSAssert1(0, #"Error while updating. '%s'", sqlite3_errmsg(newDBconnection));
else {
sqlite3_reset(updateStmt);
NSLog(#"Step successful");
}
}
else
{
NSLog(#"Error in opening database ");
}
}
There is no table Coffee in SQL.sqlite.
SQLite silently creates the database file if it does not exist. So if you've got the path wrong, you are opening an empty database file, which of course does not contain any tables. Make sure the database file exists there and it is not empty.
You can see what tables are there in the database by running SELECT * FROM sqlite_master; query.
One thing my DBA tells me when I have database problems (Oracle in this case), is to try the query using the command line tools.
This is how you would diagnose problems with sqlite3 and the iPhone simulator:
Run the App in the iPhone Simulator so the database is created/copied in the Documents directory.
Start Terminal
cd ~/Library/Application Support/iPhone Simulator/<version>/Applications/<your-app-hash>/Documents. It's best to have just one App installed in the simulator so it's obvious what App is what.
sqlite3 SQL.sqlite and try your query from there. You can also use the .schema command to see what the schema looks like.
If the query works there and not in your App then you know you have a bug, else you have a sqlite query problem or broken schema.
try reinstalling the app.the tables are created at the time of installing the app.sometimes it might happen because tables are not created
I've had a "no such table error" because the db file was never copied to the bin folder.
One the .db file in the project make sure its property "Copy to Output Directory" is set to either:
Copy always (always copies the blank database)
Copy if newer
There are probably many other questions I don't even know to ask yet since I'm new to app programming.
I initially created the database from within the app, copied it to my working folder (which is probably not where it should ultimately reside), then appended my records (about 1,000 of them) from a text file.
The first two questions that come to mind are:
- what folder should the database be in?
- how does it get deployed with the app?
I found quite few examples using the following lines in persistentStoreCoordinator function:
NSString *storePath = [[self applicationDocumentsDirectory] stringByAppendingPathComponent: #"myDatabase.sqlite"];
NSURL *storeUrl = [NSURL fileURLWithPath:storePath];
But the first line gives me the pre-compile error: "Receiver type 'NSURL' for instance message does not declare a method with selector 'stringByAppendingPathComponent:'. Why is it not working for me?
And is this in fact the best way to bundle my database with the rest of the app?
Thanks!
Easiest solution is to use NSUrl instead of NSString. SO user #trapper already provided a solution in the below link.
importing sqlite to coredata
NSURL *storeURL = [[self applicationDocumentsDirectory] URLByAppendingPathComponent:#"Database.sqlite"];
// If the database doesn't exist copy in the default one
if (![storeURL checkResourceIsReachableAndReturnError:NULL])
{
NSURL *defaultStoreURL = [[NSBundle mainBundle] URLForResource:#"Database" withExtension:#"sqlite"];
if ([defaultStoreURL checkResourceIsReachableAndReturnError:NULL])
{
NSFileManager *fileManager = [NSFileManager defaultManager];
[fileManager copyItemAtURL:defaultStoreURL toURL:storeURL error:NULL];
}
}