Searched on here and got some vague answers, so I thought i'd rephrase the problem to get some clearer answers-
Right now I have an SQL Lite db that reads/parses information from a pre-formatted .txt file. When I open the app, there is a slight 'lag' as the iDevice parses the info, then gets fetched for the iDevice. I'm just wondering if there's any way to just 'save' all the information directly in the xCode so there's no lag/fetch time?
Thank you.
What you can do is pre-build your sqlite database and then include it as a resource in your application. As this database is inside your application bundle it will be read only on your device so you will need to make a copy of it in your application document area.
I have successfully used something like the following code in a production iPhone application
NSArray *searchPaths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentFolderPath = [searchPaths objectAtIndex: 0];
NSString* dbFilePath = [documentFolderPath stringByAppendingPathComponent: DATABASE_FILE_NAME];
if(![[NSFileManager defaultManager] fileExistsAtPath: dbFilePath]) {
// copy the template database into the right place
NSError* error = nil;
[[NSFileManager defaultManager] copyItemAtPath: [[NSBundle mainBundle] pathForResource: #"template" ofType: #"db"] toPath: dbFilePath error: &error];
if(error) {
#throw [NSException exceptionWithName: #"DB Error" reason: [error localizedDescription] userInfo: nil];
}
}
int dbrc; // database return code
dbrc = sqlite3_open ([dbFilePath UTF8String], &db);
if(IS_SQL_ERROR(dbrc)) {
#throw [NSException exceptionWithName: #"DB Error" reason: NSLocalizedString(#"Could not open database", #"Database open failed") userInfo: nil];
}
Related
I want to copy files located at /Library to the folder /User/Library/AddressBook/Sample/,
I used:
[[NSFileManager defaultManager] copyItemAtPath: #"/Library/MyFile.mp3"
toPath: #"/User/Library/AddressBook/Sample/MyFile.mp3"
error: &error];
But I encountered an error that says `Operation could not be completed. No such file or
directory`
I am working on a jailbroken iPhone.
The directory:
/User/Library/AddressBook/Sample/
does not exist on a phone normally. Have you added the Sample subdirectory, before trying to copy the mp3 file into it?
With the NSFileManager methods, I would also recommend using the error object to help you debug:
NSError* error;
[[NSFileManager defaultManager] copyItemAtPath:#"/Library//MyFile.mp3" toPath: #"/User/Library/AddressBook/Sample/MyFile.mp3" error:&error];
if (error != nil) {
NSLog(#"Error message is %#", [error localizedDescription]);
}
Also, it looks like there is a mistake in your spelling of copyItemAtPath, but probably it's only in your question, and not in your code? Anyway, please double-check.
And, you have a double-slash (//) in your path, too, but I don't think that's hurting you. Just take it out, and be careful when typing :)
Update
If you are just running this app normally, but on a jailbroken phone, your app won't have access to those directories. Apps installed normally, on a jailbroken phone, still are sandboxed. The jailbreak doesn't remove all the rules on the phone. If you install the app in /Applications, like true jailbreak apps are, then that code should work for you.
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSLibraryDirectory, NSUserDomainMask, YES);
NSString *libraryDirectory = [paths objectAtIndex:0];
NSLog(#"%#",libraryDirectory); // Library path
NSString *AddressBookPath = [libraryDirectory stringByAppendingPathComponent:#"AddressBook"];
if (![[NSFileManager defaultManager] fileExistsAtPath:AddressBookPath])
{
NSError* error;
// Create "AddressBook Dir"
if([[NSFileManager defaultManager] createDirectoryAtPath:AddressBookPath withIntermediateDirectories:NO attributes:nil error:&error])
{
// Create "Sample Dir"
NSString *samplePath = [AddressBookPath stringByAppendingPathComponent:#"Sample"];
if (![[NSFileManager defaultManager] fileExistsAtPath:AddressBookPath])
{
NSError* error;
if([[NSFileManager defaultManager] createDirectoryAtPath:AddressBookPath withIntermediateDirectories:NO attributes:nil error:&error])
{
// Copy Files Now
NSError* error;
NSString *fromPath = [libraryDirectory stringByAppendingPathComponent:#"MyFile.mp3"];
NSString *toPath = [samplePath stringByAppendingPathComponent:#"MyFile.mp3"];
[[NSFileManager defaultManager] copyItemAtPath:fromPath toPath:toPath error:&error];
if (error != nil)
{
NSLog(#"Error message is %#", [error localizedDescription]);
}
}
}
}
else
{
NSLog(#"[%#] ERROR: attempting to write create MyFolder directory", [self class]);
NSAssert( FALSE, #"Failed to create directory maybe out of disk space?");
}
}
I created an sql database using "SQLite Database Browser", dragged and dropped it into my Xcode project, and built the app. It works perfectly well on the Simulator but crashes on the iPhone, with this error:
*** Terminating app due to uncaught exception 'NSInternalInconsistencyException',
reason: 'Failed to create writable database file with message 'The operation could‚
not be completed. (Cocoa error 260.)'.'
Here's my code:
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
// Creates a writable copy of the bundled default database in the application Documents directory:
NSLog(#"AppDelegate...Looking for embedded Database file...");
BOOL success;
NSFileManager *fileManager = [NSFileManager defaultManager];
NSError *error;
// Grab the path to the Documents folder:
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
NSString *writableDBPath = [documentsDirectory stringByAppendingPathComponent:#"users.sql"];
success = [fileManager fileExistsAtPath:writableDBPath];
if (success) {
NSLog(#"Database File Exists in Documents folder!");
NSLog(#"Its path is: %#", writableDBPath);
return YES;
}
else {
// But if the writable database does not exist, copy the default to the appropriate location.
NSLog(#"!!NO Database File Exists in Documents folder!");
NSString *defaultDBPath = [[[NSBundle mainBundle] resourcePath] stringByAppendingPathComponent:#"users.sql"];
success = [fileManager copyItemAtPath:defaultDBPath toPath:writableDBPath error:&error];
if (!success) {
NSAssert1(0, #"Failed to create writable database file with message '%#'.", [error localizedDescription]);
}
else
NSLog(#"WROTE THE DATABASE FILE!!!");
}
return YES;
}
Again, this works on the Simulator, but not on the iPhone. (This couldn't possible have anything to do with the file have a ".sql" extension as opposed to a ".sqlite" extension, could it? Cause that's the extensions that "SQLite Database Browser" gives the files it creates...)
The answer has to do with making sure the "Target Membership" of the sql file is set properly, so that the project "sees" it:
1) click on the sql file in the left-pane of Xcode
2) open/show the File Inspector (right pane)
3) Under "Target Membership", make sure the "check" is "checked"
that's it.
This solution resolves my problem i hope it will helps you out.
in my issue:
check this row:
NSString *writableDBPath = [documentsDirectory stringByAppendingPathComponent:#"users.sql"]
Check the fileName in your bundle and in code - the error also is cased of different filenames.
I need to write a string into a file. For that, my code is:
-(void)writeToFile:(NSString *)fileName: (NSString *)data {
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
// the path to write file
NSString *appFile = [documentsDirectory stringByAppendingPathComponent:fileName];
[data writeToFile:appFile atomically:YES];
}
I am calling this method like this
ownServices *obj = [[ownServices alloc]init];
[obj writeToFile:#"iphone.txt" :#"this is mahesh babu"];
but it didn't write into the text file.
What's the reason? Can anyone please help me.
Thank u in advance.
The most likely problem is that the documents directory does not exist. Create it if it doesn't, then write to it:
NSArray *paths = NSSearchPathForDirectoriesInDomains(
NSDocumentDirectory, NSUserDomainMask, YES);
NSString *parentDir = [paths objectAtIndex:0];
/* Create the parent directory.
* This is expected to fail if the directory already exists. */
(void)[[NSFileManager defaultManager]
createDirectoryAtPath:parentDir
withIntermediateDirectories:YES
attributes:nil error:NULL];
NSString *path = [parentDir stringByAppendingPathComponent:fileName];
/* Now write, and if it fails, you'll know why thanks to the |error| arg. */
NSError *error = nil;
BOOL ok = [data writeToFile:path options:NSDataWritingAtomic error:&error];
if (!ok) {
NSLog(#"%s: Failed to write to %#: %#", __func__, path, error);
}
Even simpler would be to use the latest API, which will create the directory for you if it doesn't already exist:
NSError *error = nil;
NSURL *parentURL = [[NSFileManager defaultManager]
URLForDirectory:NSDocumentDirectory inDomain:NSUserDomainMask
appropriateForURL:nil create:YES error:&error];
if (!parentURL) {
NSLog(#"%s: *** Failed to get documents directory: %#", __func__, error):
return;
}
NSURL *furl = [parentURL URLByAppendingPathComponent:fileName];
error = nil;
BOOL ok = [data writeToURL:furl options:NSDataWritingAtomic error:&error];
if (!ok) {
NSLog(#"%s: *** Failed to write to %#: %#", __func__, furl, error);
}
Firstly, you are calling your method strangely. Rename the method to
-(void)writeString:(NSString *) data toFile:(NSString *)fileName
and call it like so:
[obj writeString:#"this is mahesh babu" toFile:#"iphone.txt"];
Secondly, writeToFile:atomically: is deprecated, use writeToFile:atomically:encoding:error::
NSError *error = nil;
BOOL success = [data writeToFile:appFile atomically:YES encoding:NSUTF8Encoding error:&error];
if (!success) {
NSLog(#"Error: %#", [error userInfo]);
}
This way, you also see what the error is.
Your code looks OK. Use the debugger (or an NSLog statement) to verify the values of data and appFile. If data is nil, nothing will happen (including no errors) because sending a message to nil is a no-op. It's also possible that appFile is not the path you think it is.
Check the permissions of the directory you are trying to write to (ls -la). On the device you can't, but on the simulator you can. Is it read-only for you? Is it owned by another user?
Assuming that isn't the problem, try calling with atomically:NO. Atomic file writing is performed by writing a file, then renaming it to replace the old one. If the problem is there, that will isolate the problem.
Bonus Style Critique
Class names should start with an uppercase letter: OwnServices instead of ownServices
Although your method name is perfectly valid, it's unusual to have two parameters with no words to separate them. A name like writeToFile:string: would be better.
Don't name a variable data if it is meant to point to an instance of something other than NSData. It's confusing, and there's almost a better (more specific) word you can use beside "data".
My problem appears in the device but not in the simulator.
nDBres=sqlite3_prepare_v2(databaseConn, sqlStatement, -1, &compiledStatement,NULL)
It's an insert query that I'm trying to run. In the simulator it returns 0, whereas in the device it returns 8. After this whenever I try to run any other write operation, the app crashes.
I'm going nuts over this.
You can consult the list of sqlite3 error codes as part of the sqlite3 API documentation (http://www.sqlite.org/c3ref/c_abort.html. Error code 8 is SQLITE_READONLY ("Attempt to write a readonly database").
As you probably know, iOS sandboxes applications running on the device, so you must make sure to create your database in one of the areas that the OS exposes for creating application writable files.
There's a decent tutorial on how to set up a sqlite3 project on iOS http://icodeblog.com/2008/08/19/iphone-programming-tutorial-creating-a-todo-list-using-sqlite-part-1/.
From that tutorial, the most important part for your issue is probably the createEditableCopyOfDatabaseIfNeeded method in the app delegate. This illustrates how you can ensure that you create an editable database file when your app launches for the first time:
(Note, this isn't my code... I'm reproducing it from the tutorial on icodeblog.com, where they explain it in detail)
- (void)applicationDidFinishLaunching:(UIApplication *)application {
[self createEditableCopyOfDatabaseIfNeeded];
[self initializeDatabase];
// Configure and show the window
[window addSubview:[navigationController view]];
[window makeKeyAndVisible];
}
// Creates a writable copy of the bundled default database in the application Documents directory.
- (void)createEditableCopyOfDatabaseIfNeeded {
// First, test for existence.
BOOL success;
NSFileManager *fileManager = [NSFileManager defaultManager];
NSError *error;
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
NSString *writableDBPath = [documentsDirectory stringByAppendingPathComponent:#"todo.sqlite"];
success = [fileManager fileExistsAtPath:writableDBPath];
if (success) return;
// The writable database does not exist, so copy the default to the appropriate location.
NSString *defaultDBPath = [[[NSBundle mainBundle] resourcePath] stringByAppendingPathComponent:#"todo.sqlite"];
success = [fileManager copyItemAtPath:defaultDBPath toPath:writableDBPath error:&error];
if (!success) {
NSAssert1(0, #"Failed to create writable database file with message '%#'.", [error localizedDescription]);
}
}
I have a persistence problem in my application. I'm using sqlite database. when some insert queries executed results temporary added to the database. After restarting application the new values vanish! I think new values stored on RAM do not save on hard-disk.
-(IBAction)add:(id)sender
{
NSString *myDB;
NSString *query;
myDB=[[NSBundle mainBundle] resourcePath] stringByAppendingPathComponent:#"Ssozluk.sql"];
database =[[Sqlite alloc] init];
[database open:myDB];
query=[NSString stringWithFormat:#"INSERT INTO words(col_1,col_2) VALUES('asd','asd2');"];
[database executeNonQuery:query];
[database commit];
[database close];
}
-(IBAction)show:(id)sender
{
NSString *myDB;
NSString *query;
NSArray *asdasd;
NSString *asd;
myDB=[[NSBundle mainBundle] resourcePath] stringByAppendingPathComponent:#"Ssozluk.sql"];
database =[[Sqlite alloc] init];
[database open:myDB];
query=[NSString stringWithFormat:#"Select col_2,col_1 FROM words"];
asdasd=[database executeQuery:query];
for(NSDictionary *row in kelimeler)
{
asd=[row valueForKey:#"col_2"];
olabel1.text=asd;
}
[database commit];
[database close];
}
You need programmatically copy your database to Documents dir of application, and work with writable copy. Resources in bundle is readonly.
I use the following code to copy my database to the documents folder and than get the writable part:
- (void)createEditableCopyOfDatabaseIfNeeded {
// First, test for existence.
BOOL success;
NSFileManager *fileManager = [NSFileManager defaultManager];
NSError *error;
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
NSString *writableDBPath = [documentsDirectory stringByAppendingPathComponent:#"db.sqlite"];
//NSLog(writableDBPath);
success = [fileManager fileExistsAtPath:writableDBPath];
if (success) return;
// The writable database does not exist, so copy the default to the appropriate location.
NSString *defaultDBPath = [[[NSBundle mainBundle] resourcePath] stringByAppendingPathComponent:#"db.sqlite"];
success = [fileManager copyItemAtPath:defaultDBPath toPath:writableDBPath error:&error];
if (!success) {
NSAssert1(0, #"Failed to create writable database file with message '%#'.", [error localizedDescription]);
}
}
- (void)initializeDatabase {
// The database is stored in the application bundle.
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
NSString *path = [documentsDirectory stringByAppendingPathComponent:#"db.sqlite"];
// Open the database. The database was prepared outside the application.
if (sqlite3_open([path UTF8String], &database) == SQLITE_OK) {
//Add initial sql statements here
} else {
// Even though the open failed, call close to properly clean up resources.
sqlite3_close(database);
NSAssert1(0, #"Failed to open database with message '%s'.", sqlite3_errmsg(database));
// Additional error handling, as appropriate...
}
}
- (sqlite3*) getDB{
return database;
}
Insert the calls to your applicationDidFinishLaunching method
As far as I remember, when you use path to db the way you do, it should be added to project.
Sometimes, and especially with databases, just cleaning doesn't work!!!
Go to the dir: /Users/qjsi/Library/Application Support/iPhone Simulator/4.3/Applications
There, you'll find all the projects you've run. Find the folder containing the project your working on, and delete it. Then clean your project through Xcode, then run. The folder in that dir will be recreated, and so will the database.
NOTE: The database will be removed as well! If you have it saved in your bundle and copy it to an editable directory, please note that the database will be the same as the one in your bundle (so, without altered records made in the iPhone Simulator).