I use coreData in my iOS App. It's possible, that the user Add, Delete Data into the Database.
I have to deliver default data ( some different data-sets ).
At the moment, I'm creating the database by first Application launch. I read data from a csv file an create the database with it.
The csv is in the Application sandbox; the coreData (managedDocument) is in ApplicationDocument (creation on runtime...).
It works perfect for me - but I ask me, will Apple allow that, if I push the App to the AppStore?
There is nothing wrong with this approach and it can't be a reason for rejection. There is also another way to do it. You can create the database the way you do it now, copy the .sqlite file and provide it as your default database. Then copy it on app first run. The following code will do it:
NSURL *storeURL = [[self applicationDocumentsDirectory] URLByAppendingPathComponent: #"YourDBName.sqlite"];
if (![fileManager fileExistsAtPath:storeURL.path]) {
NSString *defaultStorePath = [[NSBundle mainBundle] pathForResource:#"YourDBName" ofType:#"sqlite"];
if (defaultStorePath) {
[fileManager copyItemAtPath:defaultStorePath toPath:storeURL.path error:NULL];
}
}
With this approach you will not need to include your csv file in your bundle.
Yes, apple does allow shipping a database populated by default.
the standard way to do it is to ship a default database in your bundle, then at launch time check if there is a database in your application documents directory, and if it does not exist, then copy the database from your bundle to the documents directory.
Related
I'm using Couchbase lite for my iOS app. I include the database in my app bundle and I don't have any replication. (I know it's a bit strange to use Couchbase without replication but that's the case)
The problem is that I can't find a way to access the database directly from the bundle since replaceDatabaseNamed will copy it to the document directory. As the database is huge I don't want to duplicate it on the users iPhone with one version in the bundle and one in the document directory.
Do you know a way not to copy the database and to access it directly from the app bundle ?
This works for me and successfully opens the database readonly from the bundle...
CBLManagerOptions options;
options.readOnly = YES;
_manager = [[CBLManager alloc] initWithDirectory:[[NSBundle mainBundle] resourcePath] options:&options error:&error];
if (error == nil) {
// load an existing database
_database = [_manager existingDatabaseNamed:named error:&error];
}
One thing to add here is that if you have any CBLView objects you need to make sure they get run before you save the database. They will fail to update when you are using this as a readonly database.
I have an app that downloads a whole bunch of data from over 100+ APIs upon successful login. I successfully download the data, and then use iExplorer to extract the data container folders (Documents, Library and Tmp) from the fully loaded application.
I would like to take a blank version of the original app, in .ipa format, and insert those data container folders into that fully loaded .ipa file. Then I will be able to take this new fully loaded .ipa, and use a deployment software to deploy it to a bunch of local user's devices. So everyone will have this fully loaded app.
Please, has anyone done this? Please provide some feedback, and don't argue with my methodology, because this has be done this way due to requirements. Maybe there is a step I'm missing? I'm not sure.
With the source code in hand, you can run the app in the simulator (no need for iExplorer), wait for it to download all the files and browse to the folder on your computer where the app was installed.
From there you can put aside any files you want along with their respective folders. If you're using Coredata there should be a SQLITE database file there somewhere (typically in your Application Support folder) and this might be all you need but it is hard to tell without looking at your implementation details.
Once you have the files you need set aside, add them to the app bundle via Xcode and create code to check whether files already exist (in which case you don't want to replace them), and if not copy all files needed from the bundle into their respective folders.
Here's some semi pseudo-code for you:
NSDictionary *userPrefs = [[NSUserDefaults standardUserDefaults] objectForKey:self.email];
if (![userPrefs[kInitialSetupCompleted] boolValue])
NSFileManager *fileManager = [NSFileManager defaultManager];
NSURL *destinationFilePath = ...
NSURL *seedFilePath = [NSURL fileURLWithPath:[[NSBundle mainBundle] pathForResource:kCoreDataStoreName ofType:nil]];
NSError* err = nil;
if (![fileManager copyItemAtURL:seedPath toURL:destinationFilePath error:&err]) {
NSLog(#"Could not copy seed data. error: %#", err);
// Deal with error
} else {
// Set user defaults kInitialSetupCompleted to YES
}
}
I have an app (say version 1.0) which is using Core data and now i have updated the app using sqlite and for the new update i am not using core data at all (we will call it version 1.1), however i have kept the name of my database and all the column names of the table similar to the previous core data app.
My query is that what major or minor effects will it have if the user updates to the new version (1.1) which is using sqlite. Will it have some kind of dataloss or any crashes if yes then please guide me out by providing any links or suggestions
To avoid dataloss i am planning to transfer data in chunk from core data to sqlite app as read in this post, so am i doing this right do let me know if i have missed any valuable steps.
Migrating user data stored in sqlite to core data in app upgrade
Thank you
If your actions will correct and grammar, users willn't lose any data.
How I understand, in your next version of app you will managing data by any sqlite framework, then you just need .sqlite data file. To get it .sqlite file path you can:
NSString *dataFilePath = [[[self applicationDocumentsDirectory] path]
stringByAppendingPathComponent: #"YourAppName.sqlite"];
NSURL *dataFileUrl = [NSURL fileURLWithPath:storePath];
When applicationDocumentsDirectory method implement like this:
- (NSURL *)applicationDocumentsDirectory
{
return [[[NSFileManager defaultManager] URLsForDirectory:NSDocumentDirectory inDomains:NSUserDomainMask] lastObject];
}
If I understand something incorrect, write it in comments.
I need my application work as follows:
1- User download and startup the application.
2- The application will automatically fill the (based core data) with specific records.
3- The application will works ok.
4- When the user close the application and restart it.
5_ The application will not automatically fill the core data with the specific records because it already there.
6- The user will not be able to add/remove/update the data.
Is there a good technique to do that using only core data and accepted by Apple.
Populate the data in the simulator
Make a copy of the SQLITE database from the simulator folder
Add the database as a resource to your project.
Do something like this before initializing your Core Data stack:
code:
// Put down default db if it doesn't already exist
NSFileManager *fileManager = [NSFileManager defaultManager];
if (![fileManager fileExistsAtPath:storePath]) {
NSString *defaultStorePath = [[NSBundle mainBundle]
pathForResource:#"MyDataFile" ofType:#"sqlite"];
if (defaultStorePath) {
[fileManager copyItemAtPath:defaultStorePath toPath:storePath error:NULL];
}
}
Not shown, but critical: all the files you prepopulate data for must be marked as "do not backup", or you will be rejected, per the App Store guidelines. See this SO question for some details on how to do this: Why was my application still rejected after excluding files from iCloud backup using this code?
I can tell you one very easy way to do it. In the app delegate, create a variable in NSUserDefaults and change its value when the application is loaded for the first time. Then depending upon the value of NSUserDefaults, fill the data you want in the Core Data store. And this will happen only once because the value of NSUserDefaults variable is not going to change again.
I am building a project where I need to prepopulate a coredata database with existing data.
I built a parser to create the sqlite file in the iOS Simulator, everything works fine.
I am using a single entity, and one of the attribute is indexed.
Performance after parsing my data file into core data is great, everything is good.
Now I am using the generated sqlite file (~200Mb) in project with same data model, same index, etc... and on first startup I copy over the db file to prepopulate the data
NSString *defaultStorePath = [[NSBundle mainBundle] pathForResource:#"myproject" ofType:#"sqlite"];
NSString *storePath = [[[self applicationDocumentsDirectory] path] stringByAppendingPathComponent: #"myproject.sqlite"];
NSError *error;
if (![[NSFileManager defaultManager] fileExistsAtPath:storePath])
{
if ([[NSFileManager defaultManager] copyItemAtPath:defaultStorePath toPath:storePath error:&error])
NSLog(#"Copied starting data to %#", storePath);
else
NSLog(#"Error copying default DB to %# (%#)", storePath, error);
}
The copying works fine, and the data can be accessed normally.
However the performance is terrible, and the index is clearly not being used.
A look at the size of the sqlite file after the copy operation, it went from 200Mb to 120Mb.
Everything looks alright in the model, what needs to be indexed is checked as indexed.
1) Is there a way for the index data not to be removed when copying the sqlite over?
2) Is it possible to programmatically rebuild the index?
3) Any other thoughts?
Check at the Apple documentation about this issue:
Although Core Data supports SQLite as one of its persistent store types, the database format is private. You cannot create a SQLite database using native SQLite API and use it directly with Core Data (nor should you manipulate an existing Core Data SQLite store using native SQLite API). If you have an existing SQLite database, you need to import it into a Core Data store (see “Efficiently Importing Data”).
To sum up, don't do it. The database schema is private and may change.
I use CSV files to pre-load all my initial data to CoreData on background when the application starts for the first time. Beware of multithreading CoreData access, by the way.
Hope it helps.
Looks like the problem was that the project didn't get cleaned well in between tests, it could have been a bug in Xcode 4.3 I was using at the time.
The same methodology is working fine now.