Accessing database file in xcode - iphone

I have an existing database file. I want to use this db file in a project. I placed this file in my app folder in mac and dragged the same into Xcode. And now I have written the code to check whether the file exists or not. But it is throwing FALSE exception.
bool databaseAlreadyExists = [[NSFileManager defaultManager] fileExistsAtPath:#"school.db"];
Please let me know how to use the existing db files.
Thanks

Try getting the path with
NSString *databasePath = [[NSBundle mainBundle] pathForResource:#"school" ofType:#"db"];
For Swift:
let dbPath = Bundle.main.path(forResource: "school", ofType: "db")

Without knowing what type of DB it is (MySQL, SQLITE, etc.) the best I can tell you is to find an iOS appropriate library/wrapper to access the database. As an example. FMBD if very good for SQLITE.

I suppose you're using Core Data in your project, aren't you?
If so take a look at the iPhone Core Data Recipes sample project by Apple!
In the Classes/​RecipesAppDelegate.m you'll find sample code for accomplishing what you need!
There's a good tutorial about pre-loading over at Ray Wenderlich's site.
Shipping a pre-loaded database in the way the Core Data Recipes example showcases is not the most desireable way by Apple. So please take a look at the provided tutorial for a more sophisticated approach!

It depends on whether or not you need the database to be writable.
You can get access to the database just by doing this:
NSString *defaultDBPath = [[[NSBundle mainBundle] resourcePath] stringByAppendingPathComponent:#"mydb.sqlite"];
but if you've copied it to another directory so you can write to it (e.g. the Documents directory), you'd need to do something like this to get the path:
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
NSString *path = [documentsDirectory stringByAppendingPathComponent:#"mydb.sqlite"];

Related

Including Core Data in app bundle

I'm not sure that I completely understand how Core Data works on iOS.
I have a large JSON file which I have parsed and imported into core data. This generates an sqlite file. I need this file to be included with the app but every time I delete the app from the device - I have to run the JSON parse script again to create a new sqlite file on the device. I want to be able to exclude the JSON file from the application bundle and dont want to run the parsing script on first use.
How do I go about doing this? Am I doing something wrong?
You will need to create the sqlite file (using your app if you like), then copy it into your project and deploy it with the app. You will also need to add some code to move the file into your documents directory when your app runs for the first time. It can be a simple if file doesn't exist then copy it script.
NSFileManager *fileManager = [NSFileManager defaultManager];
NSError *error;
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *DB = [[paths lastObject] stringByAppendingPathComponent:#"myDB.sqlite"];
if (![fileManager fileExistsAtPath:DB]) {
NSString *shippedDB = [[[NSBundle mainBundle] resourcePath] stringByAppendingPathComponent:#"myDB.sqlite"];
[fileManager copyItemAtPath:shippedDB toPath:DB error:&error];
}
I use this method to ship out pre-built sqlite files, although I haven't used it when CoreData is managing the sqlite file.

iPhone pathForResource vs bundlePath

I want to use pathForResource, but it doesn't look like it will create the path if one doesn't exist. Therefore I'm trying to create one manually by doing the following:
NSString *path = [NSString stringWithFormat:#"%#/%#.plist",[[NSBundle mainBundle] bundlePath],#"myFileName"];
I'm creating files dynamically, so I need to access them after I have Build and Run the application. But it puts the project in a unique id folder so the path comes out to something like:
/Users/RyanJM/Library/Application Support/iPhone Simulator/3.0/Applications/80986747-37FD-49F3-9BA8-41A42AF7A4CB/MyApp.app/myFileName.plist
But that unique id changes every time I do a build. What is the proper way to create a path that I can get to every time (even in the Simulator)?
Thanks.
Update: edited the question, hopefully to help anyone who comes across it in the future.
Update: IWasRobbed answered the proper way to get create a path URL. But the the best answer I've been able to find is from Brad Parks. Though, I do wish there was a cleaner way.
With the way you phrased your question, this is how you read a plist that has been included in the bundle before build:
NSString *propertyListPath = [[NSBundle mainBundle] pathForResource:SomeString ofType:#"plist"];
If you want to access the directories that each app has as a unique storage area for a file that you create AFTER build, you use this:
#define kFilename #”data.plist”
- (NSString *)dataFilePath {
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
return [documentsDirectory stringByAppendingPathComponent:kFilename];
}
Then you can check for it and do some data handling here:
NSString *filePath = [self dataFilePath];
if ([[NSFileManager defaultManager] fileExistsAtPath:filePath]) {
// do something with data here
}
You would save yourself a lot of trouble if you bought/read Beginning iPhone 3 Development specifically chapter 11 where he goes over data persistence (which is where this example came from). It's a great book.

iPhone Simulator - Writing to File/Reading problems

I have been reading alot on iPhone read/writing and I believe that everything I have is correct but for some reason it isn't working the way it should.
code is as follows
NSString *docsDirectory = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) objectAtIndex:0];
NSString *filePath = [docsDirectory stringByAppendingFormat:#"filename.rtf"];
NSFileManager *fileManager = [NSFileManager defaultManager];
if (![fileManager fileExistsAtPath:filePath])
I went into the folder iphone simulator documents folder for the app I am making and added that file into it as a safety measure and it is still not triggering the if statement. So for some reason it is not finding it which is making all of my writing and reading impossible. I even tried using the FileManager's createFile function and didn't get any results. If I need to post more code of something let me know but I figure if all of the above is correct it should be finding the file correctly. Thanks in advance.
UPDATE: Fixed. Hilariously simple mistake. Thanks everyone.
The NSString returned by NSSearchPathForDirectoriesInDomains does not have a trailing slash, thus the path you are building is incorrect. To append the filename to the use stringByAppendingPathComponent which will handle the path separaters correctly.
To create the filePath, try:
NSString *filePath =[docsDirector stringByAppendingPathComponent:#"filename.rtf"];
Have you tried using stringByAppendingPathComponent: instead of stringByAppendingFormat:. If docsDirectory does not contain a trailing slash, you will actually be creating a file called /PATH/TO/Documentsfilename.rtf rather than /PATH/TO/Documents/filename.rtf.

How can i change sqlite ReadOnly to ReadWrite on the iPhone?

i deployed my App to my iPhone and get
Unknown error calling sqlite3_step (8: attempt to write a readonly database) eu
on Insert / Update Statements.
On the Simulator it all works like it should.
My sqlite Database is placed in the Resource Folder (Xcode).
Thanks for help!
Your application bundle is not writable on the iPhone. You MUST copy the file somewhere else, like your documents folder. It works in the simulator because the Mac does not enforce all the sandboxing restrictions the iPhone does.
You can copy your database from the application bundle directory to the Documents directory in viewDidLoad. You can read/write from/to your database in the Documents directory after this. Of course, you need to check if the database in the Documents directory exist before you do the copy in order not to overwrite it the next time you bring up the app.
Assuming you have defined your database name '#define kFilename #"yourdatabase.db"' in the .m file.
In viewDidLoad add:
// Get the path to the main bundle resource directory.
NSString *pathsToReources = [[NSBundle mainBundle] resourcePath];
NSString *yourOriginalDatabasePath = [pathsToResources stringByAppendingPathComponent:kFilename];
// Create the path to the database in the Documents directory.
NSArray *pathsToDocuments = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [pathsToDocuments objectAtIndex:0];
NSString *yourNewDatabasePath = [documentsDirectory stringByAppendingPathComponent:kFilename];
if (![[NSFileManager defaultManager] isReadableFileAtPath:yourNewDatabasePath]) {
if ([[NSFileManager defaultManager] copyItemAtPath:yourOriginalDatabasePath toPath:yourNewDatabasePath error:NULL] != YES)
NSAssert2(0, #"Fail to copy database from %# to %#", yourOriginalDatabasePath, yourNewDatabasePath);
}
Good luck!
aobs

How can I get a writable path on the iPhone?

I am posting this question because I had a complete answer for this written out for another post, when I found it did not apply to the original but I thought was too useful to waste. Thus I have also made this a community wiki, so that others may flesh out question and answer(s). If you find the answer useful, please vote up the question - being a community wiki I should not get points for this voting but it will help others find it
How can I get a path into which file writes are allowed on the iPhone? You can (misleadingly) write anywhere you like on the Simulator, but on the iPhone you are only allowed to write into specific locations.
There are three kinds of writable paths to consider - the first is Documents, where you store things you want to keep and make available to the user through iTunes (as of 3.2):
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
Secondly, and very similar to the Documents directory, there is the Library folder, where you store configuration files and writable databases that you also want to keep around, but you don't want the user to be able to mess with through iTunes:
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSLibraryDirectory, NSUserDomainMask, YES);
NSString *libraryDirectory = [paths objectAtIndex:0];
Note that even though the user cannot see files in iTunes using a device older than 3.2 (the iPad), the NSLibraryDirectory constant has been available since iPhoneOS 2.0, and so can be used for builds targeting 3.0 (or even earlier if you are still doing that). Also the user will not be able to see anything unless you flag an app as allowing users to modify documents, so if you are using Documents today you are fine as long as you change location when updating for support of user documents.
Last there is a cache directory, where you can put images that you don't care exist for the long term or not (the phone may delete them at some point):
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES);
NSString *cachePath = [paths objectAtIndex:0];
BOOL isDir = NO;
NSError *error;
if (! [[NSFileManager defaultManager] fileExistsAtPath:cachePath isDirectory:&isDir] && isDir == NO) {
[[NSFileManager defaultManager] createDirectoryAtPath:cachePath withIntermediateDirectories:NO attributes:nil error:&error];
}
Note that you have to actually create the Caches directory there, so when writing you have to check and create every time! Kind of a pain, but that's how it is.
Then when you have a writable path, you just append a file name onto it like so:
NSString *filePath = [documentsDirectory stringByAppendingPathComponent:#"SomeDirectory/SomeFile.txt"];
or
NSString *filePath = [cachePath stringByAppendingPathComponent:#"SomeTmpFile.png"];
Use that path for reading or writing.
Note that you can make subdirectories in either of those writable paths, which one of the example string above is using (assuming one has been created).
If you are trying to write an image into the photo library, you cannot use file system calls to do this - instead, you have to have a UIImage in memory, and use the UIImageWriteToSavedPhotosAlbum() function call defined by UIKit. You have no control over the destination format or compression levels, and cannot attach any EXIF in this way.
Thanks to Kendall & Dave, above, and I thought this amendment was useful to bring up. When using for one-off debug code, I used this trick from Mike Ash's NSBlog to eliminate the temporary variables isDir & error, minimizing the number of lines and making the verbosity almost bearable:
NSFileHandle *dumpFileHandle = nil;
#ifdef DEBUG
NSString *cachePath = [NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) objectAtIndex:0];
if (![[NSFileManager defaultManager] fileExistsAtPath:cachePath isDirectory:&(BOOL){0}])
[[NSFileManager defaultManager] createDirectoryAtPath:cachePath withIntermediateDirectories:YES attributes:nil error:&(NSError*){nil}];
NSString *dumpPath = [cachePath stringByAppendingPathComponent:#"dump.txt"];
[[NSFileManager defaultManager] createFileAtPath:dumpPath contents:nil attributes:nil];
[(dumpFileHandle = [NSFileHandle fileHandleForWritingAtPath:dumpPath]) truncateFileAtOffset:0];
#endif
if (dumpFileHandle) [dumpFileHandle writeData:blah];