I have one application in that i created the sqlite database and store in the document directory. Now i open other application from existing application and in that i want to access the existing database path. how can i do this can any one help me. I write the code below for open another application and calling the document directory. But it gets the the wrong document directory path.
==============
UIApplication *ourApplication = [UIApplication sharedApplication];
NSString *ourPath = #"POSRetail://";
NSURL *ourURL = [NSURL URLWithString:ourPath];
if ([ourApplication canOpenURL:ourURL])
{
databasePath = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) objectAtIndex:0];
NSLog(#"databasePath : %#",databasePath);
NSString *filePath = [databasePath stringByAppendingPathComponent:#"testapp.sqlite"];
BOOL fileExists = [[NSFileManager defaultManager] fileExistsAtPath:filePath];
if(fileExists)
{
NSMutableString *strRecord =[[NSMutableString alloc]init];
//Retrieve the values of database
const char *dbpath = [filePath UTF8String];
if (sqlite3_open(dbpath, &sdatabase) == SQLITE_OK)
{
// NSString *querySQL = [NSString stringWithFormat:#"SELECT * FROM employee"];
// const char *query_stmt = [querySQL UTF8String];
const char *query_stmt = "Select * from info";
sqlite3_stmt *statement;
//const char *query_stmt = "Select * from sqlite_master where type='table'";
NSMutableArray *arrTemp = [[NSMutableArray alloc] init];
if(sqlite3_prepare_v2(sdatabase, query_stmt, -1, &statement, NULL) == SQLITE_OK)
{
while (sqlite3_step(statement)==SQLITE_ROW) {
[arrTemp addObject:[NSString stringWithFormat:#"%s",sqlite3_column_text(statement, 0)]];
[strRecord appendFormat:#"%#,",[NSString stringWithFormat:#"%s",sqlite3_column_text(statement, 0)]];
[arrTemp addObject:[NSString stringWithFormat:#"%s",sqlite3_column_text(statement, 1)]];
[strRecord appendFormat:#" %#,",[NSString stringWithFormat:#"%s",sqlite3_column_text(statement, 1)]];
[arrTemp addObject:[NSString stringWithFormat:#"%s",sqlite3_column_text(statement, 2)]];
[strRecord appendFormat:#" %#,",[NSString stringWithFormat:#"%s",sqlite3_column_text(statement, 2)]];
[arrTemp addObject:[NSString stringWithFormat:#"%s",sqlite3_column_text(statement, 3)]];
[strRecord appendFormat:#" %#\n",[NSString stringWithFormat:#"%s",sqlite3_column_text(statement, 3)]];
}
NSLog(#"arrTemp : %#",arrTemp);
sqlite3_finalize(statement);
}
else
{
NSAssert1(0, #"Error: failed to prepare statement with message '%s'.", sqlite3_errmsg(sdatabase));
}
sqlite3_close(sdatabase);
}
else
{
sqlite3_close(sdatabase);
NSAssert1(0, #"Failed to open database with message '%s'.", sqlite3_errmsg(sdatabase));
}
}
else
{
NSLog(#"SQLITE DATABASE NOT AVAILABLE");
}
}
else {
UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:#"Receiver Not Found" message:#"The Receiver App is not installed. It must be installed." delegate:nil cancelButtonTitle:#"OK" otherButtonTitles:nil];
[alertView show];
[alertView release];
}
As if you want to access other iOS App data then Two Words From Apple
Not Possible
As iOS App is work in SandBox Environment sharing data between applications so sharing data directly through the file system is not possible, But there is also certain ways provided for sharing data review this like
Custom URL Scheme:
the iOS programming guide now has a section on passing data between
apps by having one app claim a certain URL prefix and then having
other apps reference that URL. For this point of your you can look
this Apple Documentation. And Good Thread on SO related
this Question.
UIDocumentInteractionController
A document interaction controller, along with a delegate object,
provides in-app support for managing user interactions with files in
the local system. For example, an email program might use this class
to allow the user to preview attachments and open them in other
apps. Use this class to present an appropriate user interface for
previewing, opening, copying, or printing a specified file.
iCloud API:
iCloud is a free service that lets users access their personal
content on all
their devices—wirelessly and automatically via Apple ID. iCloud does this by
combining network-based storage with dedicated APIs, supported by full
integration with the operating system. Apple provides server infrastructure,
backup, and user accounts, so you can focus on building great iCloud-enabled
apps.
NOTE : No Idea With Jail Break iOS Device I Got the Reference This Great Document.
Currently there is no way to access the data of another application directly.
Because each application is running on a sandbox environment.
But you can do it, how ?
You need to use iCloud. You can share the data stored the iCloud between apps, so store your database file in iCloud.
References:
You can find a tutorial on iCloud here : iCloud Programming
I cloud Data sharing is described here : iCloud Data Sharing
I don't think that's possible at all, each app runs in its own VM, its impossible for other apps to access data of each other, unless the data is written on a shared space like SD card itself.
You can jailbreak your devices, then use some jailbreak development library that can give you each application's sandbox folder, or you can iterate the Application folder yourself.
Related
Here is my code to add a table in my existing database. My code reaches to success callback of sqlite3_prepare_v2 and gives the log table created, but when I open my sqlite database using terminal, I see that there were no tables created. My database table name is fz1.sqlite declared by pragma by this: #define kDBName #"fz1.sqlite"
-(sqlite3 *)mydb:(NSString *)query{
static sqlite3 *database;
static sqlite3_stmt *enableForeignKey;
if (database == NULL) {
sqlite3 *newDBconnection;
// first get the path for the document directory of the application
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *dbpath = [paths objectAtIndex:0];
NSString *path = [dbpath stringByAppendingPathComponent:kDBName];
if (sqlite3_open([path UTF8String], &newDBconnection) == SQLITE_OK) {
NSLog(#"Database Successfully Opened :)");
database = newDBconnection;
if (sqlite3_prepare_v2(database,[query UTF8String], -1, &enableForeignKey, NULL) != SQLITE_OK) {
NSLog(#"ERROR IN PRAGMA!");
}
else{
sqlite3_exec(database, [query UTF8String], NULL, NULL, NULL);
NSLog(#"faiz's table created");
}
sqlite3_finalize(enableForeignKey);
} else {
NSLog(#"Error in opening database :(");
database = NULL;
}
}
return database;
}
It seems no error with your implementation. I think you are opening the db file placed in the Application bundle and it will bot be updated with the queries you performing.
Once you launch your application the database file will be copied to the device/simulator 's document directory. So if you are running your application in iOS Simulator check the db in the document directory folder.
Open it and check whether updated or not.
Example of the folder structure for Document Dir with db file iOS Simulator
/Users/XYZ/Library/Application Support/iPhone
Simulator/7.1/Applications/B5EC1893-B38F-4AB4-AE7C-48EF685EE35F/Documents/db.sqlite
If you're putting parameters into your query using [NSString stringWithFormat:#"YourSqlStatement %# %#",parameter1,parameter2]; or something similar before calling -(sqlite3 *)mydb:(NSString *)query you might be SQL injecting yourself. If you are doing this then try using sqlite_bind_text to load your parameters and then sqlite_step to execute.
If this isn't relevant to your implementation try logging the output of sqlite_exec and have a look at your error message.
Reasons
2.23: Apps must follow the iOS Data Storage Guidelines or they will be rejected
2.23
We found that your app does not follow the iOS Data Storage Guidelines, which is required per the App Store Review Guidelines.
In particular, we found that on launch and/or content download, your app stores 64 MB. To check how much data your app is storing:
Install and launch your app
Go to Settings > iCloud > Storage & Backup > Manage Storage
If necessary, tap "Show all apps"
Check your app's storage
The iOS Data Storage Guidelines indicate that only content that the user creates using your app, e.g., documents, new files, edits, etc., should be backed up by iCloud.
Temporary files used by your app should only be stored in the /tmp directory; please remember to delete the files stored in this location when the user exits the app.
Data that can be recreated but must persist for proper functioning of your app - or because customers expect it to be available for offline use - should be marked with the "do not back up" attribute. For NSURL objects, add the NSURLIsExcludedFromBackupKey attribute to prevent the corresponding file from being backed up. For CFURLRef objects, use the corresponding kCFURLIsExcludedFromBackupKey attribute.
For more information, please see Technical Q&A 1719: How do I prevent files from being backed up to iCloud and iTunes?.
It is necessary to revise your app to meet the requirements of the iOS Data Storage Guidelines.
For discrete code-level questions, you may wish to consult with Apple Developer Technical Support. Please be sure to:
include the complete details of your rejection issues
prepare any symbolicated crash logs, screenshots, and steps to reproduce the issues for when the DTS engineer follows up.
For information on how to symbolicate and read a crash log, please see Tech Note TN2151 Understanding and Analyzing iPhone OS Application Crash Reports.
If you have difficulty reproducing this issue, please try testing the workflow as described in https://developer.apple.com/library/ios/qa/qa1764/Technical Q&A QA1764: How to reproduce a crash or bug that only App Review or users are seeing.
and i set this function in AppDelegate.m
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
NSString *pathCaches = [NSHomeDirectory() stringByAppendingPathComponent:#"Library/Caches"];
NSString *pathTmp = [NSHomeDirectory() stringByAppendingPathComponent:#"/tmp"];
NSString *pathDocuments = [NSHomeDirectory() stringByAppendingPathComponent:#"/Documents"];
[self addSkipBackupAttributeToItemAtURL:[NSURL fileURLWithPath:pathCaches]];
[self addSkipBackupAttributeToItemAtURL:[NSURL fileURLWithPath:pathTmp]];
[self addSkipBackupAttributeToItemAtURL:[NSURL fileURLWithPath:pathDocuments]];
return YES;
}
- (BOOL)addSkipBackupAttributeToItemAtURL:(NSURL *)URL
{
assert([[NSFileManager defaultManager] URLsForDirectory:NSCachesDirectory inDomains:NSUserDomainMask]);
const char* filePath = [[URL path] fileSystemRepresentation];
const char* attrName = "com.mycompany.MyApp";
u_int8_t attrValue = 1;
int result = setxattr(filePath, attrName, &attrValue, sizeof(attrValue), 0, 0);
return result == 0;
}
The .sqlite3 file is not downloaded so I putted it in /Documents and other pdf files and images are downloaded
and my application rejected with the same reason
Is i forget something ???
Please help me
Thank You
In case you want to submit for iOS versions 5.0.1 and above, use the below code. I don't think that you need to skip cache and tmp directory from iCloud backup coz iCloud doesn't care about these directories.
+(BOOL)addSkipBackupAttributeToItemAtURL:(NSURL *)fileURL {
// First ensure the file actually exists
if (![[NSFileManager defaultManager] fileExistsAtPath:[fileURL path]]) {
NSLog(#"File %# doesn't exist!",[fileURL path]);
return NO;
}
// Determine the iOS version to choose correct skipBackup method
NSString *currSysVer = [[UIDevice currentDevice] systemVersion];
if ([currSysVer isEqualToString:#"5.0.1"]) {
const char* filePath = [[fileURL path] fileSystemRepresentation];
const char* attrName = "com.apple.MobileBackup";
u_int8_t attrValue = 1;
int result = setxattr(filePath, attrName, &attrValue, sizeof(attrValue), 0, 0);
NSLog(#"Excluded '%#' from backup",fileURL);
return result == 0;
}
else if (&NSURLIsExcludedFromBackupKey) {
NSError *error = nil;
BOOL result = [fileURL setResourceValue:[NSNumber numberWithBool:YES] forKey:NSURLIsExcludedFromBackupKey error:&error];
if (result == NO) {
NSLog(#"Error excluding '%#' from backup. Error: %#",fileURL, error);
return NO;
}
else { // Succeeded
NSLog(#"Excluded '%#' from backup",fileURL);
return YES;
}
} else {
// iOS version is below 5.0, no need to do anything
return YES;
}
}
If the data can be downloadable from Server,then instead of saving your downloaded files to the Documents directory save them to the Cache directory which is a temp directory that don't get backed up to iCloud and can be randomly deleted by the OS on certain occasions.
The problem is here:
const char* attrName = "com.mycompany.MyApp";
Dont change the value with your bundle id, it should always be:
const char* attrName = "com.apple.MobileBackup";
I want to use a pre populated database with my web-app, so that my app works offline. How can i do this with the newest version of PhoneGap / Cordova (2.0)?
I know that this question has been asked before, but all answers seem to be out of date relative to the current version of cordova and iOS
https://github.com/atkinson/phonegap-prepopulate-db has not been updated for two years
https://github.com/davibe/Phonegap-SQLitePlugin has not been updated for 7 months and is for 1.7
I found a post here:
http://www.raymondcamden.com/index.cfm/2012/7/27/Guest-Blog-Post-Shipping-a-populated-SQLite-DB-with-PhoneGap is this the only way?
Also, i should note that i use iOS 6
I have used the following plugin for Cordova 2.7 with iOS6 and I see it was just updated a day ago. https://github.com/pgsqlite/PG-SQLitePlugin-iOS
They also have an Android version here as well.
Part of the problem is PhoneGap changes so often keeping plugins up to date can be time consuming so they lapse. Here is the code for the init method in the AppDelegate.m I use in this project http://www.binpress.com/app/conference-core-ios-for-phonegap/1483
Keep in mind that you sometimes need to wipe the iOS Simulator so it reloads the files related to the application.
- (id)init{
NSHTTPCookieStorage* cookieStorage = [NSHTTPCookieStorage sharedHTTPCookieStorage];
[cookieStorage setCookieAcceptPolicy:NSHTTPCookieAcceptPolicyAlways];
int cacheSizeMemory = 8 * 1024 * 1024; // 8MB
int cacheSizeDisk = 32 * 1024 * 1024; // 32MB
if __has_feature(objc_arc)
NSURLCache* sharedCache = [[NSURLCache alloc] initWithMemoryCapacity:cacheSizeMemory diskCapacity:cacheSizeDisk diskPath:#"nsurlcache"];
else
NSURLCache* sharedCache = [[[NSURLCache alloc] initWithMemoryCapacity:cacheSizeMemory diskCapacity:cacheSizeDisk diskPath:#"nsurlcache"] autorelease];
endif
[NSURLCache setSharedURLCache:sharedCache];
databaseName = #"SomeCoolDatabase.db";
NSLog(#"databaseName: %#", databaseName);
databasePath = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) objectAtIndex: 0];
NSLog(#"databasePath: %#", databasePath);
databaseFile = [databasePath stringByAppendingPathComponent:databaseName];
NSLog(#"databaseFile: %#", databaseFile);
// Execute the "checkAndCreateDatabase" function
[self checkAndCreateDatabase];
self = [super init];
return self;
}
-(void)checkAndCreateDatabase {
// Check if the SQL database has already been saved to the users phone, if not then copy it over
BOOL success;
// Create a FileManager object, we will use this to check the status
// of the database and to copy it over if required
NSFileManager *fileManager = [NSFileManager defaultManager];
// Check if the database has already been created in the users filesystem
success = [fileManager fileExistsAtPath:databaseFile];
// If the database already exists then return without doing anything
if(success){
NSLog(#"Database Present");
return;
} else {
NSLog(#"database not present");
}
// If not then proceed to copy the database from the application to the users filesystem
// Get the path to the database in the application package
NSString *databasePathFromApp = [[[NSBundle mainBundle] resourcePath] stringByAppendingPathComponent:databaseName];
// Create the database folder structure
[fileManager createDirectoryAtPath:databasePath withIntermediateDirectories:YES attributes:nil error:NULL];
// Copy the database from the package to the users filesystem
[fileManager copyItemAtPath:databasePathFromApp toPath:databaseFile error:nil];
[fileManager release];
}
Hope this helps...
Thank you! I wouldn't have been able to do this without starting here!
That said, this wasn't working for me out of the box, so I updated it, and I'm by no means 'good' with native code for iOS, so I'm VERY open to suggestions.
I added the suggested code to the AppDelegate.m file, got a heap of errors.
So, I moved over to the AppDelegate.h file:
#interface AppDelegate : NSObject <UIApplicationDelegate>{} --Looks like this to start.
Make it look like this:
#interface AppDelegate : NSObject <UIApplicationDelegate>{
NSString* databaseName;
NSString* databasePath;
NSString* databaseFile;
}
I just had to declare the variables in there.
I also removed this line:
[fileManager release];
As, according to this answer, my project seemingly automatically releases? I hope?
Now I have to work on getting the DB file into the correct location for xcode to find it.
Hope this helps!
Though this question is old... thought it might help someone to post my solution - found in 2017. (Since PhoneGap has changed, older solutions no longer will work or be accepted on the Android and Apple app markets.) I worked on this problem for a long time - the issue is you want to import a pre-populated database - which there are not alot of easy solutions for. I found the best and easiest - and currently just about the ONLY - solution to be LiteHelpers CORDOVA-SQLITE-EXT (https://github.com/litehelpers/cordova-sqlite-ext)
I have added this in to my app and it is working well. I had to switch away from PhoneGap though - and use CORDOVA - but it is working for me.
My app was rejected cause it seems that 7 mb are stored in documents folder and they are automatically send to icloud. So i have looped all files that will be written to documents folder throught this method :
- (BOOL)addSkipBackupAttributeToItemAtURL:(NSURL *)URL {
const char* filePath = [[URL path] fileSystemRepresentation];
const char* attrName = "com.apple.MobileBackup";
if (&NSURLIsExcludedFromBackupKey == nil) {
// iOS 5.0.1 and lower
u_int8_t attrValue = 1;
int result = setxattr(filePath, attrName, &attrValue, sizeof(attrValue), 0, 0);
return result == 0;
}
else {
// First try and remove the extended attribute if it is present
int result = getxattr(filePath, attrName, NULL, sizeof(u_int8_t), 0, 0);
if (result != -1) {
// The attribute exists, we need to remove it
int removeResult = removexattr(filePath, attrName, 0);
if (removeResult == 0) {
NSLog(#"Removed extended attribute on file %#", URL);
}
}
// Set the new key
NSError *error = nil;
[URL setResourceValue:[NSNumber numberWithBool:YES] forKey:NSURLIsExcludedFromBackupKey error:&error];
return error == nil;
}
The version 1.1 of my app was approved after this code implementation. Last week i tried to send the version 1.2 of the same app (nothing has changed in file management, all files that are stored in documents folder are looped through the addSkipBackupAttributeToItemAtURL method). My app was rejected again for the same reason. I can't move my file to temp or cache folder because my app can't completely restore the file (one of this file is a db, restoring db means loose any user inserted data), so this one can't be the solution. Anyway i have found an issue in the code, this is how i call the method :
[self addSkipBackupAttributeToItemAtURL:[NSURL fileURLWithPath:fullPath]];
using [NSURL fileURLWithPath:fullPath] device with ios 5.1 return an error and it seems impossible to create the attribute. If i change the initialization of the nsurl with [NSURL URLWithString:defaultStorePath], device with 5.1 seems to add correctly the attribute.
With ios 5.0.1 all is inverted , [NSURL URLWithString:defaultStorePath] return an error while [NSURL fileURLWithPath:fullPath] works.
Maybe i can check ios version and set an appropriate nsurl initialization, but it still remains a problem. In rejection explanation i read :
In particular, we found that on launch and/or content download, your app stores 7mb. To check how much data your app is storing:
Install and launch your app
Go to Settings > iCloud > Storage & Backup > Manage Storage
If necessary, tap "Show all apps"
Check your app's storage
If i try to check this value i see 7 mb also with the correct nsurl initialization (when all the attributes are set correctly) . What is the correct behaviour? Anyone with this problem? Do I have to do something specific before the app storage check suggested by apple to make it significant?
I think the trick is to add the NSURLIsExcludedFromBackupKey AND make sure the directory is outside the documents directory. I did this by moving my documents to the Library/Application Support folder (since it didn't make sense in the /tmp or /Caches folders):
// store in /Library/Application Support/BUNDLE_IDENTIFIER/Reference
// make sure Application Support folder exists
NSURL *applicationSupportDirectory = [[NSFileManager defaultManager] URLForDirectory:NSApplicationSupportDirectory
inDomain:NSUserDomainMask
appropriateForURL:nil
create:YES
error:&error];
if (error) {
NSLog(#"KCDM: Could not create application support directory. %#", error);
return nil;
}
NSURL *referenceFolder = [applicationSupportDirectory URLByAppendingPathComponent:#"Reference" isDirectory:YES];
if (![[NSFileManager defaultManager] createDirectoryAtPath:[referenceFolder path]
withIntermediateDirectories:YES
attributes:nil
error:&error]) {
NSLog(#"KCDM: Error creating Reference folder to store model %#: %#", modelName, error);
return nil;
}
BOOL success = [referenceFolder setResourceValue:#YES forKey: NSURLIsExcludedFromBackupKey error: &error];
if(!success){
NSLog(#"KCDM: Error excluding %# from backup %#", referenceFolder, error);
}
I had the same problem as you until I deleted my app from my device, and re-installed. I also had to delete the existing cached data from the iCloud backup by going to Settings->Storage&Backup -> Manage Storage
That seemed to do the trick.
Also, my code to add the skip attribute is a bit different:
Code lifted from this post
- (BOOL)addSkipBackupAttributeToItemAtURL:(NSURL *)URL
{
assert([[NSFileManager defaultManager] fileExistsAtPath: [URL path]]);
if (&NSURLIsExcludedFromBackupKey == nil) { // iOS <= 5.0.1
const char* filePath = [[URL path] fileSystemRepresentation];
const char* attrName = "com.apple.MobileBackup";
u_int8_t attrValue = 1;
int result = setxattr(filePath, attrName, &attrValue, sizeof(attrValue), 0, 0);
return result == 0;
}
else { // iOS >= 5.1
NSError *error = nil;
[URL setResourceValue:[NSNumber numberWithBool:YES] forKey:NSURLIsExcludedFromBackupKey error:&error];
return error == nil;
}
}
I have an app with a pre-filled .sqlite file that is copied into the user's Documents directory when the app is first opened. This file is 12.9MB. Twice now, my app has been rejected since changing target to iOS5 with this rejection note:
Binary Rejected Apr 24, 2012 10:12 AM
Reasons for Rejection:
2.23 Apps must follow the iOS Data Storage Guidelines or they will be rejected
Apr 24, 2012 10:12 AM. From Apple.
2.23
We found that your app does not follow the iOS Data Storage Guidelines, which is required per the App Store Review Guidelines.
In particular, we found that on content download, your app stores 12.81 MB. To check how much data your app is storing:
Install and launch your app
Go to Settings > iCloud > Storage & Backup > Manage Storage
If necessary, tap "Show all apps"
Check your app's storage
The iOS Data Storage Guidelines indicate that only content that the user creates using your app, e.g., documents, new files, edits, etc., may be stored in the /Documents directory - and backed up by iCloud.
Temporary files used by your app should only be stored in the /tmp directory; please remember to delete the files stored in this location when the user exits the app.
Data that can be recreated but must persist for proper functioning of your app - or because customers expect it to be available for offline use - should be marked with the "do not back up" attribute. For NSURL objects, add the NSURLIsExcludedFromBackupKey attribute to prevent the corresponding file from being backed up. For CFURLRef objects, use the corresponding kCFURLIsExcludedFromBackupKey attribute.
For more information, please see Technical Q&A 1719: How do I prevent files from being backed up to iCloud and iTunes?.
It is necessary to revise your app to meet the requirements of the iOS Data Storage Guidelines.
I have tried setting the "do not back up" attribute as recommended in the Data Storage Guidelines, but is was rejected again.
I do not use iCloud in my app, and Settings > iCloud > etc. shows no usage at all.
I cannot use the Caches or tmp directories as the database is modified by the user after creation.
I seem to be between a rock and a hard place here with Apple not allowing this kind of app to function at all.
Has anyone had this problem and managed to overcome it?
EDIT 17-5-12
I still haven't managed to get this app approved yet. Has anyone managed to do this?
EDIT 1-7-12
My app has just been rejected again for the same reason. I am at a loss as to what to do here, as surely it is a common use scenario.
EDIT 11-9-12
App now approved - please see my solution below. I hope it can help someone else.
OK, here is the solution I managed to get approved (finally!)
This is the code for setting the Skip Backup attribute - note that it is different for 5.0.1 and below and 5.1 and above.
#include <sys/xattr.h>
- (BOOL)addSkipBackupAttributeToItemAtURL:(NSURL *)URL
{
if (&NSURLIsExcludedFromBackupKey == nil) { // iOS <= 5.0.1
const char* filePath = [[URL path] fileSystemRepresentation];
const char* attrName = "com.apple.MobileBackup";
u_int8_t attrValue = 1;
int result = setxattr(filePath, attrName, &attrValue, sizeof(attrValue), 0, 0);
return result == 0;
} else { // iOS >= 5.1
NSError *error = nil;
[URL setResourceValue:[NSNumber numberWithBool:YES] forKey:NSURLIsExcludedFromBackupKey error:&error];
return error == nil;
}
}
And here is my persistentStoreCoordinator
- (NSPersistentStoreCoordinator *)persistentStoreCoordinator {
if (__persistentStoreCoordinator != nil)
{
return __persistentStoreCoordinator;
}
NSURL *storeURL = [[self applicationDocumentsDirectory] URLByAppendingPathComponent:#"store.sqlite"];
NSError *error;
NSFileManager *fileManager = [NSFileManager defaultManager];
NSString *storePath = [[[self applicationDocumentsDirectory] path] stringByAppendingPathComponent:#"store.sqlite"];
// For iOS 5.0 - store in Caches and just put up with purging
// Users should be on at least 5.0.1 anyway
if ([[[UIDevice currentDevice] systemVersion] isEqualToString:#"5.0"]) {
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES);
NSString *cacheDirectory = [paths objectAtIndex:0];
NSString *oldStorePath = [storePath copy];
storePath = [cacheDirectory stringByAppendingPathComponent:#"store.sqlite"];
storeURL = [NSURL URLWithString:storePath];
// Copy existing file
if ([fileManager fileExistsAtPath:oldStorePath]) {
[fileManager copyItemAtPath:oldStorePath toPath:storePath error:NULL];
[fileManager removeItemAtPath:oldStorePath error:NULL];
}
}
// END iOS 5.0
if (![fileManager fileExistsAtPath:storePath]) {
// File doesn't exist - copy it over
NSString *defaultStorePath = [[NSBundle mainBundle] pathForResource:#"store" ofType:#"sqlite"];
if (defaultStorePath) {
[fileManager copyItemAtPath:defaultStorePath toPath:storePath error:NULL];
}
}
NSDictionary *options = [NSDictionary dictionaryWithObjectsAndKeys: [NSNumber numberWithBool:YES], NSMigratePersistentStoresAutomaticallyOption, [NSNumber numberWithBool:YES], NSInferMappingModelAutomaticallyOption, 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();
}
[self addSkipBackupAttributeToItemAtURL:storeURL];
return __persistentStoreCoordinator;
}
Note that I made the decision to just store in Caches and put up with purging for iOS 5.0 users.
This was approved by Apple this month.
Please don't copy and paste this code without reading and understanding it first - it may not be totally accurate or optimised, but I hope it can guide someone to a solution that helps them.
#define SYSTEM_VERSION_GREATER_THAN_OR_EQUAL_TO(v) ([[[UIDevice currentDevice] systemVersion] compare:v options:NSNumericSearch] != NSOrderedAscending)
#include <sys/xattr.h>
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
//Put this in your method
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
NSURL *pathURL= [NSURL fileURLWithPath:documentsDirectory];
iOS5 = NO;
if (SYSTEM_VERSION_GREATER_THAN_OR_EQUAL_TO(#"5.0.1")) {
iOS5 = YES;
}
// Set do not backup attribute to whole folder
if (iOS5) {
BOOL success = [self addSkipBackupAttributeToItemAtURL:pathURL];
if (success)
NSLog(#"Marked %#", pathURL);
else
NSLog(#"Can't marked %#", pathURL);
}
}
- (BOOL)addSkipBackupAttributeToItemAtURL:(NSURL *)URL
{
const char* filePath = [[URL path] fileSystemRepresentation];
const char* attrName = "com.apple.MobileBackup";
u_int8_t attrValue = 1;
int result = setxattr(filePath, attrName, &attrValue, sizeof(attrValue), 0, 0);
return result == 0;
}
You said that you don't use iCloud. In that case, you should simply move your sqlite file to a directory with suffix .nosync. That should do it!
NSString *dataFileName = #"my.sqlite";
NSString *dataFileDirectoryName = #"Data.nosync";
NSString *documentsDirectoryPath = [self applicationDocumentsDirectory];
NSFileManager *fileManager = [NSFileManager defaultManager];
if ([fileManager fileExistsAtPath:[documentsDirectoryPath stringByAppendingPathComponent:dataFileDirectoryName]] == NO) {
NSError *fileSystemError;
[fileManager createDirectoryAtPath:[documentsDirectoryPath stringByAppendingPathComponent:dataFileDirectoryName]
withIntermediateDirectories:YES
attributes:nil
error:&fileSystemError];
if (fileSystemError != nil) {
NSLog(#"Error creating database directory %#", fileSystemError);
}
}
NSString *dataFilePath = [[documentsDirectoryPath
stringByAppendingPathComponent:dataFileDirectoryName]
stringByAppendingPathComponent:dataFileName];
// Move your file at dataFilePath location!
HTH.
I've been looking into this and have found this very interesting article on the subject : http://iphoneincubator.com/blog/data-management/local-file-storage-in-ios-5
I believe that you will continue to be rejected if you try to use the /Documents folder to store your DB file.
I would suggest you bite the bullet and use /Cache. The worst user case scenario would be that their device runs low on memory and the app gets cleaned removing the DB file. In this case when your app launches again it should copy over the bundled DB file into /Cache and the user would sync to go and grab the excess data from your remote server. That is assuming this is how your app works.
To get your app to move your DB file from /Documents to /Cache you can just tell NSFileManager to do this for you ...
#define FILE_MANAGER [NSFileManager defaultManager]
#define DOCUMENTS_PATH [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) objectAtIndex: 0]
#define CACHES_PATH [NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) objectAtIndex: 0]
#define DB_DOCS_PATH [DOCUMENTS_PATH stringByAppendingPathComponent: #"database.db"]
#define DB_PATH [CACHES_PATH stringByAppendingPathComponent: #"database.db"]
if([FILE_MANAGER moveItemAtPath: DB_DOCS_PATH toPath:DB_PATH error:&error])
{
NSLog(#"SUCCESSFULLY MOVED %# to %#",DB_DOCS_PATH,DB_PATH);
}
This will prevent your existing users from using their iCloud storage unnecessarily.