Accessing an sqlite3 file with Core Data - iphone

I'm doing the second part of Ray Wenderlich's Core Data Tutorial. Instead of reading the file that contains the default data, I'm trying to read the actual Failed Bank datafile that was created in his SQLite3 tutorial. The program is throwing an exception when I try to create the Store and open the File.
Here is the code snippet
- (NSPersistentStoreCoordinator *) persistentStoreCoordinator
{
if (_persistentStoreCoordinator != nil) {
return _persistentStoreCoordinator;
}
NSURL *storeURL = [[self applicationDocumentsDirectory] URLByAppendingPathComponent:#"banklist.sqlite3"];
if (![[NSFileManager defaultManager] fileExistsAtPath:[storeURL path]]) {
NSURL *preloadURL = [NSURL fileURLWithPath:[[NSBundle mainBundle] pathForResource:#"banklist" ofType:#"sqlite3"]];
NSError *err = nil;
if (![[NSFileManager defaultManager] copyItemAtURL:preloadURL toURL:storeURL error:&err]) {
NSLog(#"Opps, couldn't copy preloaded data");
}
}
NSError *error = nil;
_persistentStoreCoordinator = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel:[self managedObjectModel]];
if (![_persistentStoreCoordinator addPersistentStoreWithType:NSSQLiteStoreType configuration:nil URL:storeURL options:nil error:&error]) {
NSLog(#"Unresolved error %#, %#", error, [error userInfo]);
abort();
}
return _persistentStoreCoordinator;
}
Here is the exception:
2013-09-16 20:19:57.549 FailedBankCD[4086:c07] CoreData: error: (1) I/O error for database at /Users/artletter1/Library/Application Support/iPhone Simulator/6.1/Applications/524B84E3-94D6-40A3-8847-DD15A99B22A2/Documents/banklist.sqlite3. SQLite error code:1, 'no such table: Z_METADATA'
2013-09-16 20:19:57.552 FailedBankCD[4086:c07] Unresolved error Error Domain=NSCocoaErrorDomain Code=256 "The operation couldn’t be completed. (Cocoa error 256.)" UserInfo=0x854b560 {NSUnderlyingException=I/O error for database at /Users/artletter1/Library/Application Support/iPhone Simulator/6.1/Applications/524B84E3-94D6-40A3-8847-DD15A99B22A2/Documents/banklist.sqlite3. SQLite error code:1, 'no such table: Z_METADATA', NSSQLiteErrorDomain=1}, {
NSSQLiteErrorDomain = 1;
NSUnderlyingException = "I/O error for database at /Users/artletter1/Library/Application Support/iPhone Simulator/6.1/Applications/524B84E3-94D6-40A3-8847-DD15A99B22A2/Documents/banklist.sqlite3. SQLite error code:1, 'no such table: Z_METADATA'";
}
Thanks!
Roy

To accomplish what you intend to do you will have to go through the tables of your sqlite file using the sqlite3 API, insert new Core Data entities, fill them with the data and save to a different file.
.

Related

"The operation couldn’t be completed. (Cocoa error 512.)"

I have this code, which should be working perfectly, but I can't udnerstand why it isn't:
+(NSString *)writeImageToFile:(UIImage *)image {
NSData *fullImageData = UIImageJPEGRepresentation(image, 1.0f);
NSString *path = [NSHomeDirectory() stringByAppendingPathComponent:#"Documents/Images/"];
NSFileManager *fileManager = [NSFileManager defaultManager];
BOOL isDirectory = NO;
BOOL directoryExists = [fileManager fileExistsAtPath:path isDirectory:&isDirectory];
if (directoryExists) {
NSLog(#"isDirectory: %d", isDirectory);
} else {
NSError *error = nil;
BOOL success = [fileManager createDirectoryAtPath:path withIntermediateDirectories:NO attributes:nil error:&error];
if (!success) {
NSLog(#"Failed to create directory with error: %#", [error description]);
}
}
NSString *name = [NSString stringWithFormat:#"%#.jpg", [JEntry generateUuidString]];
NSString *filePath = [path stringByAppendingPathComponent:name];
NSError *error = nil;
BOOL success = [fullImageData writeToFile:filePath options:NSDataWritingAtomic error:&error];
if (!success) {
NSLog(#"Failed to write to file with error: %#", [error description]);
}
return filePath;
}
It passed the directoryExists without an error, but when it gets to writeToFile, it gives me this error:
Error Domain=NSCocoaErrorDomain Code=512 "The operation couldn’t be completed. (Cocoa error 512.)" UserInfo=0x5634ee0 {NSFilePath=/var/mobile/Applications/5E25F369-9E05-4345-A0A2-381EDB3321B8/Documents/Images/18DAE0BD-6CB4-4244-8ED1-9031393F6DAC.jpg, NSUnderlyingError=0x5625010 "The operation couldn’t be completed. Not a directory"}
Any ideas why this might be?
I was able to reproduce your error when writing a file first in the path #"Documents/Images/", then trying to write the image using your code.
I think there are two possible scenarios for this:
1) You created that file by mistake at a previous execution of your app. This will be solved if you reset the simulator using the menu: iOS Simulator > Reset Contents and Settings, and uninstalling the app from your device: Long press > click on the x symbol.
2) There is some code somewhere else in your app that creates this file. If this is the case, you should find this code and remove it.
From FoundationErrors.h:
NSFileWriteUnknownError = 512
Try using withIntermediateDirectories:YES.
In my case a period '.' in the directory name (e.g. ~/Documents/someDir.dir/somefile) was the cause of the problem. I removed the offending character and the error disappeared.

How to import a pre-existing sqlite file into Core Data?

I need to import a .sqlite file into Core Data, I searched on the internet and found:
Core Data Tutorial: How To Preload/Import Existing Data
It is creating a Python script to populate this database by reading in the contents of our old database, and creating the appropriate rows in the new database. But my sqlite database is too large in term of number of tables and columns, this may cost me a considerable amount of time.
I also found this:
Using a Pre-Populated SQLite Database with Core Data on iPhone OS 3.0
But I don't quite understand it, it looks like it's copying old database to a new one, then how does it add Z_ suffix to all the table and column names? Also, it asks me to create entities and attributes, is there anyway this can be done automatically(from sqlite dabase file)?
Thanks!
This answers here might be useful (mine is one of them)
Pre-populate Core Data
/**
Returns the path to the application's Documents directory.
*/
- (NSString *)applicationDocumentsDirectory {
return [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) lastObject];
}
sample code
// Returns the persistent store coordinator for the application.
// If the coordinator doesn't already exist, it is created and the application's store added to it.
- (NSPersistentStoreCoordinator *)persistentStoreCoordinator
{
if (_persistentStoreCoordinator != nil)
{
return _persistentStoreCoordinator;
}
NSURL *storeURL = [[self applicationDocumentsDirectory] URLByAppendingPathComponent:#"yourSqlite.sqlite"];
NSString *sourcePath = [[NSBundle mainBundle] pathForResource:#"yourSqlite.sqlite" ofType:nil];
NSString *documentsDirectory = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) objectAtIndex:0];
NSError *error = nil;
if (![[NSFileManager defaultManager] fileExistsAtPath:[documentsDirectory stringByAppendingPathComponent:#"yourSqlite.sqlite"] ]) {
if([[NSFileManager defaultManager] copyItemAtPath:sourcePath toPath:[documentsDirectory stringByAppendingPathComponent:#"yourSqlite.sqlite"] error:&error]){
NSLog(#"Default file successfully copied over.");
} else {
NSLog(#"Error description-%# \n", [error localizedDescription]);
NSLog(#"Error reason-%#", [error localizedFailureReason]);
}
}
_persistentStoreCoordinator = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel:[self managedObjectModel]];
if (![_persistentStoreCoordinator addPersistentStoreWithType:NSSQLiteStoreType configuration:nil URL:storeURL options:nil error:&error])
{
NSLog(#"Unresolved error %#, %#", error, [error userInfo]);
abort();
}
return _persistentStoreCoordinator;
}

XCode4 - where to look for sqlite file created by core data

I have just started using core data. I want to setup a pre-populated db. I read somewhere that core data creates a sqlite file when a core data app is run. I don't know where to look for it though.
I followed the instructions over on this blog but did not find the sqlite file over the location specified directory /Users/<Username>/Library/Application Support/iPhone Simulator/User/Application/<Application GUID>/Documents/<database name.sqlite> nor in the application directory.
here is my code for persistentCoordinator.
- (NSPersistentStoreCoordinator *)persistentStoreCoordinator
{
if (__persistentStoreCoordinator != nil)
{
return __persistentStoreCoordinator;
}
NSString *storePath = [[[self applicationDocumentsDirectory] path] stringByAppendingPathComponent:#"coredata.sqlite"];
NSFileManager *fileManager = [NSFileManager defaultManager];
if (![fileManager fileExistsAtPath:storePath]) {
NSString *defaultStorePath = [[NSBundle mainBundle] pathForResource:#"coredata" 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];
NSError *error = nil;
__persistentStoreCoordinator = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel:[self managedObjectModel]];
if (![__persistentStoreCoordinator addPersistentStoreWithType:NSSQLiteStoreType configuration:nil URL:storeURL options:options error:&error])
{
NSLog(#"Unresolved error %#, %#", error, [error userInfo]);
abort();
}
return __persistentStoreCoordinator;
}
Those files are in ~/Library/Application Support/iPhone Simulator/[SDK version]/Applications/[App GUID]/Documents for me, both for Xcode 3 and Xcode 4.
If you have multiple SDKs, make sure to look in all the different SDK version directories for your app.
For me it was in whatMuregSaid/[Application Guid]/Library/Application Support/[AppName]/filename.sqlite
Follow these steps:
In your applicationDocumentsDirectory function in AppDelegate.swift add this code:
let urls = NSFileManager.defaultManager().URLsForDirectory(.DocumentDirectory, inDomains: .UserDomainMask)
// Look for this in the console, this will print out the location of the sqlite database, which you can then open with an SQLite viewer
print("urls.last \(urls.last)")
Look for this in the console, this will print out the location of the sqlite database, open your terminal and open whatever_the_location_path_was
Download SQLite viewer http://sqlitebrowser.org/
Right click the yourapp.sqlite file in the folder, choose Open With sqlitebrowser that you downloaded
Cheers!
hope this will help you little more
/Users/Username/ then press cmd+shift+G and write /Users/Username/Library, Now you will see Library folder after it go to Application Support/iPhone Simulator/7.1(or 7.1-64)/Applications/F84D4CC8-326E-4A2E-8A37-F1A755D6FCC4/Documents
you will see three file and one is .sqlite file.
To see structure and other information of this sqlite file , the most efficient way is use SQLITE MANAGER(add on of firefox https://addons.mozilla.org/en-US/firefox/addon/sqlite-manager/) add it and then click TOOL .

Why am I getting a write error when copying a Core Data database between iPhone and iPad?

In my app I create and use a normal persistent store:
- (NSPersistentStoreCoordinator *) persistentStoreCoordinator {
// D_IN;
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: #"Dances005.sqlite"]];
NSError *error = nil;
if (![persistentStoreCoordinator addPersistentStoreWithType:NSSQLiteStoreType configuration:nil URL:storeUrl
options:options error:&error]){
NSLog(#"Unresolved error %#, %#", error, [error userInfo]);
}
// D_OUT;
return persistentStoreCoordinator;
..nothing special. It contains several NSData objects - all core data compliant on iPhone and iPAD.
I create the file on the iPhone and would like to copy it to the iPAD. When I open the same app on the iPAD it uses the copied sqlite and works fine as long as it reads/fetches. As soon as I change the data and need to store back I get the following error:
Unresolved error Error Domain=NSCocoaErrorDomain Code=256 UserInfo=0x6a2b7a0 "Operation could not be completed. (Cocoa error 256.)", {
NSFilePath = "/var/mobile/Applications/294B329D-D287-4012-A551-4E7348624225/Documents/Dances005.sqlite";
NSUnderlyingException = error during SQL execution : attempt to write a readonly database;
Currently I use iPhoneExplorer and just drag the file from iPhone -> MAC -> iPAD. I checked the rw Attributes with iFile and they are rw for the user on the iPAD. (same as on iPhone)
What could be the problem here and how can I solve this?
iPhoneExplorer is not supported by Apple and "just drag[ging] the file from iPhone -> MAC -> iPAD" is not supported. If you want to include the file on your iPad application then include it as part of the bundle or use another mechanism to transfer the file.

Implementation of "Automatic Lightweight Migration" for Core Data (iPhone)

I would like to make my app able to do an automatic lightweight migration when I add
new attributes to my core data model.
In the guide from Apple this is the only info on the subject I could find:
Automatic Lightweight Migration
To request automatic lightweight
migration, you set appropriate flags
in the options dictionary you pass in
addPersistentStoreWithType:configuration:URL:options:error:.
You need to set values corresponding
to both the
NSMigratePersistentStoresAutomaticallyOption
and the
NSInferMappingModelAutomaticallyOption
keys to YES:
NSError *error;
NSURL *storeURL = <#The URL of a persistent store#>;
NSPersistentStoreCoordinator *psc = <#The coordinator#>;
NSDictionary *options = [NSDictionary dictionaryWithObjectsAndKeys:
[NSNumber numberWithBool:YES], NSMigratePersistentStoresAutomaticallyOption,
[NSNumber numberWithBool:YES], NSInferMappingModelAutomaticallyOption, nil];
if (![psc addPersistentStoreWithType:<#Store type#>
configuration:<#Configuration or nil#> URL:storeURL
options:options error:&error]) {
// Handle the error.
}
My NSPersistentStoreCoordinator is initialized in this way:
- (NSPersistentStoreCoordinator *)persistentStoreCoordinator {
if (persistentStoreCoordinator != nil) {
return persistentStoreCoordinator;
}
NSURL *storeUrl = [NSURL fileURLWithPath: [[self applicationDocumentsDirectory] stringByAppendingPathComponent: #"FC.sqlite"]];
NSError *error = nil;
persistentStoreCoordinator = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel:[self managedObjectModel]];
if (![persistentStoreCoordinator addPersistentStoreWithType:NSSQLiteStoreType configuration:nil URL:storeUrl options:nil error:&error]) {
NSLog(#"Unresolved error %#, %#", error, [error userInfo]);
abort();
}
return persistentStoreCoordinator;
}
I am having trouble seeing where and how I should add the Apple code to get the Automatic Lightweight Migration working?
This is what I did to make Automatic Lightweight Migration (Source: http://brainwashinc.wordpress.com/2010/01/18/iphone-coredata-automatic-light-migration/)
1. Set the Persistent Store options for automatic migration in the app delegate.
Change your persistentStoreCoordinator creation to this (replace YOURDB):
- (NSPersistentStoreCoordinator *)persistentStoreCoordinator {
if (persistentStoreCoordinator != nil) {
return persistentStoreCoordinator;
}
NSURL *storeUrl = [NSURL fileURLWithPath: [[self applicationDocumentsDirectory] stringByAppendingPathComponent: #"YOURDB.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]) {
// Handle error
}
return persistentStoreCoordinator;
}
2. Version your Data Model and Edit the new file.
Select your xcdatamodel file
Design -> Data Model -> Add Model Version (expand your xcdatamodeld item)
Select the “2″ (or later) file, Design -> Data Model -> Set Current Version (edit this version)
3. Specify the momd resource in app delegate.
Change your managedObjectModel implementation to this (replace YOURDB)
- (NSManagedObjectModel *)managedObjectModel {
if (managedObjectModel != nil) {
return managedObjectModel;
}
NSString *path = [[NSBundle mainBundle] pathForResource:#"YOURDB" ofType:#"momd"];
NSURL *momURL = [NSURL fileURLWithPath:path];
managedObjectModel = [[NSManagedObjectModel alloc] initWithContentsOfURL:momURL];
return managedObjectModel;
}
At first, the above solution didn't work for me. The returned managedObjectModel was 0x0.
I think this is because I renamed the file names of the different model files.
If you follow the instructions above to the letter then it all works.
However if you do change model file names then you can select the "current" model file manually:
Lets say your original model file was MYMODEL.xcdatamodel
after doing the add model step above this turns into a directory MY.xcdatamodeld
and below it you have MYMODEL.xcdatamodel and MYMODEL 2.xcdatamodel
rename the new model file to whatever you want, for example lets say you removed the space to MYMODEL2.xcdatamodel and edit its content.
Now in the above code do
NSString *path = [mainBundle pathForResource:#"MYMODEL2" ofType:#"mom" inDirectory:#"MYMODEL.momd"];
I think this adds onto the last answer.
I found the usage of the bundle resource and .sqlite names really confusing at first. Does the bundle resource name change with the version change? Does the .sqlite name change? I've now got my migration working, and learned that the bundle model name refers to the name of the directory/folder in XCode containing all the models, not the name of the model versions within that directory.
When you give a modelResource name to:
NSURL *modelURL = [[NSBundle mainBundle] URLForResource:modelResource withExtension:#"momd"];
NSManagedObjectModel *theManagedObjectModel = [[NSManagedObjectModel alloc] initWithContentsOfURL:modelURL];
That modelResource name is the directory/folder for the models in Xcode.
When you do:
NSURL *storeURL = [[self applicationDocumentsDirectory] URLByAppendingPathComponent:storeFileName];
NSError *error = nil;
if (![persistentStoreCoordinator addPersistentStoreWithType:NSSQLiteStoreType configuration:nil URL:storeURL options:options error:&error]) {
// handle error
}
The storeFileName is the name of your .sqlite file in the Documents folder/directory (this is not in the bundle).
Also, when you migrate from one model version to another model version, by default, the .sqlite file name remains the same.
Oscar, in response to your issue, I found the same thing initially. I would suggest deleting and re-adding the new .xcdatamodeld file to your project, and then rebuilding. Hope that helps!
Swift 3 Solution
1. Set the Persistent Store options for automatic migration in the app delegate.
Change your persistentStoreCoordinator creation to this (replace SingleViewCoreData.sqlite):
lazy var persistentStoreCoordinator: NSPersistentStoreCoordinator = {
let coordinator: NSPersistentStoreCoordinator = NSPersistentStoreCoordinator(managedObjectModel: self.managedObjectModel)
let url = self.applicationDocumentsDirectory.appendingPathComponent("SingleViewCoreData.sqlite")
let options = [
NSMigratePersistentStoresAutomaticallyOption : Int(true),
NSInferMappingModelAutomaticallyOption : Int(true)
]
do {
try coordinator.addPersistentStore(ofType: NSSQLiteStoreType, configurationName: nil, at: url, options: options)
} catch {
print(error)
}
return coordinator
}()
2. Version your Data Model and Edit the new file.
Select your xcdatamodel file Editor>Add Model Version - add a name for your new model