iPhone pathForResource vs bundlePath - iphone

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.

Related

Accessing database file in xcode

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"];

write into plist file using NSDictionary object

Sorry I saw similar questions but they don't seem to have some full answers for me. And i try to put it in order so that people will not hate me or my poor english.
I am working with Xcode 4.2 with storyboard and ARC
I can read from my plist file. My task is simply to write back the updated value(s) to my plist file.
My plist is contain in "supporting files" sub folder of the main folder (where story-board is things goes). the file is call Global.plist and GlobalValue2 is a element of the file type string.
So the read file part looks like this
NSString *plistfile = [[NSBundle mainBundle] pathForResource:#"Global" ofType:#"plist"];
NSDictionary *dict = [NSDictionary dictionaryWithContentsOfFile:plistfile];
FirstValueTextBox.text = [[dict valueForKey:#"GlobalValue1"] stringValue];
learn it from some handy youtube video works just fine. updates the value to my text box.
The real problem comes in when I write back my plist file. When i try the following
NSString *plistfile = [[NSBundle mainBundle] pathForResource:#"Global" ofType:#"plist"];
NSMutableDictionary *dict = [NSMutableDictionary
dictionaryWithContentsOfFile:plistfile];
[dict setValue:#"ABC" forKey:#"GlobalValue2"];
SecondValueTextBox.text = [dict valueForKey:#"GlobalValue2"];
[dict writeToFile:plistfile atomically:YES];
the result is I really saw a updated value pop up on the second text box, but the plist file remain unchanged.
The following are the break down of my questions and my guess for the problem
I try to use NSDictionary(not NSMutableDictionary) and call setValue (crash in runtime)
my guess: NSDictionary object itself is readonly so it crash me when i say add value. But why don't it error me when in coding time? if the object is readonly
I use NSMutableDictionary can call setValue. it doesn't crash me and when i call the updated value at "SecondValueTextBox.text = [dict valueForKey:#"GlobalValue2"];" it really return me the updated value. but the content inside of the plist file is not changed. Which is the result I have right now.
my guess: after some search here and there I think "supporting files" is read only too. pure guess did see anyone directly talk about it.
I did try to move on a little more and some people talks about a "document folder" in Xcode that is a read and write place. I think people also talk about write a code to access that folder. Can someone show me the code here.
My last question, can I hook up my Xcode to that "document folder" or where can i see it(the real file folder structure is different from inside Xcode I think). So that i can see and edit my plist file for testing, and i can see the real result without using codes and stuff
I will be much appreciated if people can tell me my guess is right or wrong and the answer to my 3 and 4 question.
In order for your changes to be persisted in your plist, you would indeed need ot copy it from the resource bundle to the documents dirtectory on launch of the application, then use the plist in the documents to read and write.
Here is how you can copy the file:
NSFileManager *fileManager = [NSFileManager defaultManager];
NSError *error;
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
NSString *plistPath = [documentsDirectory stringByAppendingPathComponent:#"Global.plist"];
if ([fileManager fileExistsAtPath:plistPath] == NO) {
NSString *resourcePath = [[NSBundle mainBundle] pathForResource:#"Global" ofType:#"plist"];
[fileManager copyItemAtPath:resourcePath toPath:plistPath error:&error];
}

About NSSearchPathForDirectoriesInDomains

I hv spent quite a while but no luck on following question:
I tried to access Document directory.
Both following codes work perfectly. Could someone tell me whats the difference between
last object and objectAtIndex:0
NSArray *searchPaths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentPath = [searchPaths lastObject];
or
NSString *documentPath = [searchPaths objectAtIndex:0];
The most close question was How to get URL for application's document directory iPhone, but it didn't explain the difference between lastObject and objectAtIndex:0.
I have read thru Apple Developer Library. It only says 'The directory returned by this method may not exist. This method simply gives you the appropriate location for the requested directory. Depending on the application’s needs, it may be up to the developer to create the appropriate directory and any in between.' Someone please help, thx.
In this case, there is no difference between [searchPaths lastObject] and [searchPaths objectAtIndex:0], because searchPaths contains only one entry. The user domain only contains one document directory.
If you try this you'll get different results:
NSArray *searchPaths = NSSearchPathForDirectoriesInDomains(
NSApplicationDirectory, NSAllDomainsMask, YES);
searchPaths may contain more than one directory if you look with NSAllDomainsMask.
Since Xcode 5, best practice is:
NSString *documentPath = [searchPaths firstObject];

iPhone DropBox API: How to load a file?

A very basic question concerning dropBox integration into an iPhone app.
I followed the setup of the DropBoxSDK and everything works fine. I can log on to my account and get it linked. So I set up everything correctly.
Now I would like to use it to simply load a file from the dropBox and save it again. Consider that you only want to sync ONE FILE (for the sake of simplicity), called 'example.txt' which is located in the 'Example' folder in my DropBox. The same 'example.txt' is saved locally on the iPhone in the Documents directory of my app.
The dropBox readme file suggest vaguely the following code which I find highly cryptic and can't really see how to load or save a file:
2. Make an request on the rest client:
[[self restClient] loadMetadata:#"/"];
3. Implement the DBRestClientDelegate methods needed to get the results of the
particular call you made:
- (void)restClient:(DBRestClient*)client
loadedMetadata:(DBMetadata*)metadata {
NSLog(#"Loaded metadata!");
}
- (void)restClient:(DBRestClient*)client
metadataUnchangedAtPath:(NSString*)path {
NSLog(#"Metadata unchanged!");
}
- (void)restClient:(DBRestClient*)client
loadMetadataFailedWithError:(NSError*)error {
NSLog(#"Error loading metadata: %#", error);
}
So my (hopefully) simple question is how I can:
check if there is an example folder in my dropbox
if not, create one and save the example.txt from app documents into this example folder
load example.txt
once programme quits: save example.txt to DropBox
I can't really find an answer to these quite basic steps in the DropBox docs on the website. The example they've provided I find too confusing... especially as it is only about loading files and not saving them as far as I can see.
I'd be grateful for any help or suggestions of how to go about this.
Ok, I found this method to save my example.txt file:
-(void) DBupload:(id)sender
{
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0]; // Get documents directory
NSString *filePath = [documentsDirectory stringByAppendingPathComponent:#"Example.txt"];
[self.restClient uploadFile:#"NoteBook.txt" toPath:#"/example" fromPath:filePath];
}
Turns out, no need to create a folder, dropbox will do this automatically for you if it doesn't exist.
This is for downloading the same file form dropbox:
-(void) DBdownload:(id)sender
{
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0]; // Get documents directory
NSString *filePath = [documentsDirectory stringByAppendingPathComponent:#"Example.txt"];
NSError *error;
[self.restClient loadFile:#"/example/Example.txt" intoPath:filePath];
if (filePath) { // check if file exists - if so load it:
NSString *tempTextOut = [NSString stringWithContentsOfFile:filePath
encoding:NSUTF8StringEncoding
error:&error];
}
}
Hope this helps if you were struggling with a similar question.
Into the DBdownload function you can skip the check by implementing the DBRestClientDelegate method loadedFile and loadFileFailedWithError

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];