My app must start for end users with data already in the database so that info is displayed to them when they use the app.
My problem: how can I load the data into the app database?
There's an "import..." option on xcode (on Editor when selecting xcdatamodeld) but I cannot understand what is the file type required - I've tried .xls, .csv and .sqlite and none is "importable".
Help!
Add the database to your bundle when you ship your app. On initial startup, you look to see if the database lives in the local file system. If not (and it won't be the first time you start up), you copy the database from the bundle to the local file system, open it, and use it from there. Don't try to use it from the bundle, that's set to read only.
I'm assuming you're using a SQLite database or some other type of file. You can add a file to your bundle using these instructions: How do I add files to the resources folder in XCode?
If the data is already in the .sqlite format (see note below), copy that file to the project (move the file to the Xcode project directory, and then from Xcode choose File -> Add files to "Project name", and find the file in your computer directory.
In the main AppDelegate.m file under the persistentStoreCoordinator function (see this tutorial for setting up sqlite/Core Data in your project), write the code to copy from the main bundle resources to the app resources directory, if the file doesn't exist:
NSURL *dbUrl = [NSURL fileURLWithPath: [[self applicationDocumentsDirectory]
stringByAppendingPathComponent: #"yourDBName.sqlite"]];
if (![[NSFileManager defaultManager] fileExistsAtPath:[dbUrl path]]) {
NSURL *bundleURL = [NSURL fileURLWithPath:[[NSBundle mainBundle] pathForResource:#"yourDBName" ofType:#"sqlite"]];
NSError* err = nil;
if (![[NSFileManager defaultManager] copyItemAtURL:bundleURL toURL:dbUrl error:&err]) {
NSLog(#"Could not copy main bundle data");
}
else { NSLog(#"Main bundle data successfully copied"); }
}
It is important that your preloaded .sqlite database from the main bundle have been created properly from within an Xcode project (iOS apps only recognize specially-formatted .sqlite files).
Once this is loaded, it will be recognized as the app's database as long as the Core Data managed object context has its persistent store coordinator set to this .sqlite file, and that its structure matches that of the core data model.
Related
I’ve found a couple of similar questions to this, but nothing that quite fits my needs. I’ve developed a simple digitizing app in Swift that presents an image and allows me to digitize specific points in the image using the simulator.
I use Core Data to save the data. One entity (“Coursemap”) has attributes for the image (Binary Data) and some meta data (Strings). The Coursemap entity has a one-to-many relationship with a “Points” entity. The Points entity defines attributes for the x/y coordinates for specific points in the image. The digitizer app is working well; I can digitize and save several images using Core Data. In the final version of my app I’ll probably need about 50-60 digitized images.
What I’d like to do is use the data from the output of my digitizer app as input to an iPad game I’m developing. So I basically want to copy the Core Data files created in my digitizer project over to my game project so my game app can use the digitized images. Is there a way to do this? Is Core Data the best way to handle this?
What I’ve tried so far: I name the two projects the same, but keep them in separate folders, e.g.:
~/digitizer/myApp and ~/games/myApp.
The digitizer core data files are created in the folder:
~/Library/Developer/CoreSimulator/Devices/…/Documents.
When I run the game app, it seems to expect the Core Data files to be in the same directory (which is why I named the two projects the same). When the game app tries to fetch data, I get the error, “The model used to open the store is incompatible with the one used to create the store”. I’ve double-checked the data models and they are identical.
Any ideas on this? Thanks.
I realized something similar:
- pick your prefilled sqlite file from the simulator folder
- copy it over to your other project
- during runtime, check on your other project if a sqlite structure exists and if it doesn't create it by copying the sqlite file from your bundle to the documents folder:
if(![[NSFileManager defaultManager]fileExistsAtPath:coreDataDatabase.path])
{
url = coreDataDatabase;
[[NSFileManager defaultManager] createDirectoryAtURL:url withIntermediateDirectories:YES attributes:nil error:NULL];
url = [url URLByAppendingPathComponent:#"StoreContent"];
[[NSFileManager defaultManager] createDirectoryAtURL:url withIntermediateDirectories:YES attributes:nil error:NULL];
url = [url URLByAppendingPathComponent:#"persistentStore"];
// copy the sqlite file to /Documents/<YourApp>/StoreContent/persistentStore
NSArray *stringParts = [kCoreDataPreloadedDatabase componentsSeparatedByString:#"."];
NSString *sqlitePath = [[NSBundle mainBundle]
pathForResource:[stringParts objectAtIndex:0] ofType:#"sqlite"];
NSError *anyError = nil;
BOOL success = [[NSFileManager defaultManager]
copyItemAtPath:sqlitePath toPath:[url path] error:&anyError];
if(!success)
{
Error(#"Unable to copy sqlite file: %#", anyError);
}
}
I have created a SQLite file using FireFox sqliteManager and imported it to my application.
I need to copy this SQLite file from bundle to document directory. By using the below code, I am able to copy the SQLite file to document directory.
fIlMan=[NSFileManager defaultManager];
data=[fIlMan contentsAtPath:[[NSBundle mainBundle]pathForResource:#"mydatabase" ofType:#"sqlite"]];
pAth=[[[NSBundle mainBundle]resourcePath]stringByAppendingPathComponent:#"localDBFile"];
NSLog(#"%.2f",(float)data.length/1024.0f/1024.0f);
if ([fIlMan fileExistsAtPath:pAth]==NO) {
[fIlMan createFileAtPath:pAth contents:data attributes:Nil];
}
if (sqlite3_open([pAth UTF8String], &newDB)==SQLITE_OK) {
NSLog(#"DataBase successfully Created");
}
else{
NSLog(#"DataBase Failed to Create");
}
sqlite3_close(newDB);
Now if I run it in simulator its fine(nicely working). When I run this same on device, the application will terminate and show the warning as
Terminated due to memory pressure
Guidance needed on this. Thanks.
Use the copyItemAtPath:toPath:error: method of the file manager so that the file isn't loaded into memory.
Also, be careful about the destination path as [[[NSBundle mainBundle]resourcePath]stringByAppendingPathComponent:#"localDBFile"] is not a suitable way to get the document directory. Instead you should be using NSSearchPathForDirectoriesInDomains or URLsForDirectory:inDomains:.
I am trying to load an initial database into my app so my core data db is not empty upon install. I'm now using this code:
NSFileManager *fileManager = [NSFileManager defaultManager];
// If the expected store doesn't exist, copy the default store.
if (![fileManager fileExistsAtPath:storePath]) {
NSString *defaultStorePath = [[NSBundle mainBundle] pathForResource:[NSString stringWithFormat:#"%#_InitialData", SQL_DATABASE_NAME] ofType:#"sqlite"];
if (defaultStorePath) {
[fileManager copyItemAtPath:defaultStorePath toPath:storePath error:NULL];
}
}
(from http://code.google.com/p/coredatalibrary/wiki/LoadingInitialData)
to try and load an initial sqlite file for my core data to use. It isn't working and my program differs from the type used in that link in a few ways.
It appears the tutorial uses a file created to "use core data", which I am not. I just didn't happen to learn it that way (watched the stanford cs193p videos) and instead I'm using UIManagedDocument and performing this code in my top view controller. Because of this, I've run into a few problems.
I loaded up my app to create the initial data base so I could save the file to use for initial values. Upon doing so, I found that the way things are saved are different from in the tutorial. For example, if my url for my UIManagedDocument is .../Documents/Test , then my database file is .../Documents/Test/Store Content/persistentStore, where "persistentStore" is the database file. For one thing, a "Store Content" directory has been added. In addition, the sqlite file is named persistentStore and has no file extension. When I open the file it says
SQLite format 3���# �����������������������������������������������������������������-‚%
on the top though (I'm not familiar with SQLite or any db format for that matter but I assume this means it is an sqlite file).
I save this "persistentStore" file to use to load into my app using the code above. Upon doing so, I found that copyItemAtPath:toPath:error: will not copy as I expect. For example, if the storePath is .../Documents/Test , then my sqlite file that I'm copying over becomes renamed to Test.sqlite and is located at .../Documents/ instead of copying my file to a location of .../Documents/Test/.
Because of this, when I try to open the UIManagedDocument at that url (.../Documents/Test) I get this error
*** Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason:
'UIManagedDocument can only read documents that are file packages'
I've tried creating a directory to mimic the one created by core data before copying over my intial data (that is, I create the directories to have a path of .../Documents/Test/Store Content/ and then copy my initial data to be in the "Store Content" folder with a name of persistentStore) but that also doesn't work. UIManagedDocument can't open the document.
So how can I load in initial values to my core data db without having a project that is set to "use core data"? I have the (presumably) sqlite file with the initial data (when I open it and skim it it appears to have my initial values), so I just need to know how to copy it over properly so that I can still used UIManagedDocument to open the document and save via the UIManagedObjectContext.
It turns out my way of recreating the path created by core data worked. That is, creating the directories of .../Test/Store Content/ and copying the file as "persistentStore". My error was a small one. It was suppose to be StoreContent instead of "Store Content"; I added a space...
I had read that I can mark folders with "do not backup" attribute.
As I understand, in such case all contents of directory will be excluded from backups.
In our app we need to exclude from backup all files in Documents directory (the files can be added or deleted from Documents during app execution).
We need to store our files in Documents because we use "Application supports iTunes file sharing" feature.
Can we mark Documents directory with "do not backup attribute"?
Does Apple permits this?
Could this become the reason to reject our app?
According to apple
In iOS 5.0 and earlier, put files in the
/Library/Caches directory to prevent them from being
backed up
In iOS 5.0.1 and later, put files in the
/Library/Application Support directory and apply the
com.apple.MobileBackup extended attribute to them. This attribute prevents the files from being backed up to iTunes or iCloud. If you
have a large number of support files, you may store them in a custom
subdirectory and apply the extended attribute to just the directory.
As far as I know
You can not mark documents directory with do not back up attribute
1)you may mark up the individual files inside the documents directory using below code snippet
- (BOOL)addSkipBackupAttributeToItemAtPath:(NSString *)filePathString {
NSURL *fileURL =
[NSURL fileURLWithPath:filePathString];
assert([[NSFileManager defaultManager]
fileExistsAtPath: [fileURL path]]);
NSError *error = nil;
BOOL success = [fileURL setResourceValue:[NSNumber numberWithBool:YES]
forKey:NSURLIsExcludedFromBackupKey
error:&error];
return success;
}
2)You may create a subdirectory inside documents folder and apply extended attribute to that.
you may set extended attribute using the below syntax.
int result = setxattr(path, attrName, myDataBytes, [myData length], 0, 0);
you can find more information on reading and writing extended attributes in the following link
I hope this helps
My app is using a core data SQLite database. I would like to enable my users to use iCloud to sync it between devices - and I was thinking I could use UIManagedDocument.
I subclassed it, following Apple's documentation, and it is works when a new persistent store file needs to be created. However, when I try to use it to open my old persistent store file, I get the following exception thrown error:
"UIManagedDocument can only read documents that are file packages"
Does this mean that I need to migrate the old persistent store to a new store managed by UIManagedDocument? If so, do I need to do this manually (i.e. read each record one-at-a-time from the old store and write it into the new one)?
Thanks in advance!
UIManagedDocument creates packages(folders) rather than atomic stores. The store is still there but its buried in the package. If you right click on the file that is created in your Documents folder in the simulator you'll be able to see the structure. The default is
mydocument.foo
-> StoreContent
-> persistentStore
What you need to do is create a new extension for your app file type so for example if your database extension is .myappdb you need to create a new document type in your project settings which might be .myappdbw. You can copy all settings from the entry for .myappdb
Next at the point where you handle opening your legacy document at mydocumenturl instead of passing that to your persistent store co-ordinator you create the directory structure above.
NSURL *newurl = [[mydocumenturl URLByDeletingPathExtension] URLByAppendingPathExtension:#"myappdbw"];
NSURL *desturl = [newurl URLByAppendingPathComponent:#"StoreContent"];
[[NSFileManager defaultManager] createDirectoryAtURL:desturl withIntermediateDirectories:YES attributes:nil error:NULL];
NSURL *finalurl = [desturl URLByAppendingPathComponent:#"persistentStore"];
and then move the legacy database into the folder system you have created
[[NSFileManager defaultManager] moveItemAtURL:mydocumenturl toURL:finalurl error:NULL];
and then you can pass the bundle url to UIManagedDocument
UIManagedDocument *doc = [[UIManagedDocument alloc] initWithFileURL:newurl];
A link which will be useful for the iCloud integration is
http://developer.apple.com/library/ios/#releasenotes/DataManagement/RN-iCloudCoreData/_index.html
Its all a bit mysterious as the most of the promised sample code has failed to appear so far but on the other hand its mostly fairly simple to deduce. Have a look at WWDC2011 sessions 107,116 and 315 for more hints.
But note that if you are going to use this method for migrating your legacy docs DONT set the NSPersistentStoreUbiquitousContentNameKey at point you migrate because the package changes when you do. The doc above describes it quite well.
Thanks for this tip. I think I found an even simpler solution.
I just create a new UIManagedDocument with a different filename than my old persistent store location.
In my subclassed UIManagedDocument, I override the configurePersistentStoreCoordinatorForURL method and do the migration once there:
- (BOOL)configurePersistentStoreCoordinatorForURL:(NSURL *)storeURL ofType:(NSString *)fileType modelConfiguration:(NSString *)configuration storeOptions:(NSDictionary *)storeOptions error:(NSError **)error
{
// If legacy store exists, copy it to the new location
NSFileManager* fileManager = [NSFileManager defaultManager];
if ([fileManager fileExistsAtPath:legacyPersistentStoreURL.path])
{
NSError* thisError = nil;
[fileManager copyItemAtURL:legacyPersistentStoreURL toURL:storeURL error:&thisError];
[fileManager removeItemAtURL:legacyPersistentStoreURL error:&thisError];
}
return [super configurePersistentStoreCoordinatorForURL:storeURL ofType:fileType modelConfiguration:configuration storeOptions:storeOptions error:error];
}