I'm trying to access a resource file I added in the app using relative pathing. I've read that I was supposed to use something like this :
NSBundle *mainBundle = [NSBundle mainBundle];
NSString *filePath = [[mainBundle resourcePath] stringByAppendingPathComponent:#"myFile.txt"];
fh = [NSFileHandle fileHandleForReadingAtPath:resourcePath];
Except this isn't working. With NSLog, I am able to confirm mainBundle isn't null and resourcePath returns something like this:
/Users/tom/Library/Developer/CoreSimulator/Devices/XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXX/data/Containers/Bundle/Application/XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXX/MyApp_Demo.app/myFile.txt
I've tried many things, such as adding the name of directory where the resource is located, that would be Ressources/myFile, but nothing is yielding any result.
I'm a total beginner with Objective-C but I have to tinker with legacy code and I have to deal with this, so any help is much appreciated.
Side note:
this is what the project structure looks like from Xcode:
Meanwhile, in Finder, the Ressources directory isn't inside the MyApp directory, rather they're on the same level inside the project directory. I wonder if that could be the problem.
Side note 2:
NSString *filePath2 = [[NSBundle mainBundle] pathForResource:#"myFile" ofType:#"txt"];
returns (null) after logging in the console.
It is best to use a simulator for this debugging process
Verification
You should check if the file or Resources folder is actually being copied to the right location or not. If you have added the Resources folder, than check it with the below code
NSString *resourcesFolderPath = [[NSBundle mainBundle] pathForResource:#"Resources" ofType:nil];
NSString *fullFilePath = [NSString pathWithComponents:#[demoToursPath,"filename.txt"]];
NSFileManager *manager=[NSFileManager defaultManager];
NSLog(#"Filepath: %#", fullFilePath);
NSLog(#"File Exist: %#", [manager fileExistsAtPath:fullFilePath]);
Once you have confirmed this, you can update your code to match the location and path it needs to be in order to access the file.
Additional Debug
You can also just print the Document Directory for the application and open a finder window and navigate there to see if the Resources folder is added at the right place (if it was added)
NSString *documentDir = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES)objectAtIndex:0]
NSLog(#"Document Directory: %#", documentDir);
If you update the question with more info, I would be able to help you better
Related
I have to share my OS X app, all the paths I have used for files used by project are linked to my username and the structure of my computer.
Is there a way to have paths related to my project so that once my project is shared the user may not get in troubles caused by 'file not found'.
I would move the used files of the project, into the project but then I don't know how to let this happen:
actual paths, what I use now:
/Users/???username???/XCode/projectName/fileName.txt
what I would like to use in my code:
function(path: fileName.txt)
how don't know how to make the paths this short, not caring about the users directories since the files I'm going to use are all inside my project.
I am very confused. Thank you.
There is actually an easy way to read files from your project directory:
NSError *error = nil;
NSString *filePath = [[NSBundle mainBundle] pathForResource:#"filename" ofType:#"txt"];
NSString *dataContent = [NSString stringWithContentsOfFile:filePath encoding:NSUTF8StringEncoding error:&error];
So in dataContent you have the content of the file as an NSString instance.
I'm using this code to read data from a local (in the XCode project) .plist file.
NSError *error;
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
NSString *path = [documentsDirectory stringByAppendingPathComponent:#"GameData.plist"];
NSFileManager *fileManager = [NSFileManager defaultManager];
if (![fileManager fileExistsAtPath: path])
{
NSString *bundle = [[NSBundle mainBundle] pathForResource:#"GameData" ofType:#"plist"];
[fileManager copyItemAtPath:bundle toPath: path error:&error];
}
gameData = [[NSMutableDictionary alloc] initWithContentsOfFile: path];
And it worked great the start, but now I find when I step through the code in the simulator that my game is using a version of the .plist file that existed right at the start, and the new fields I've set up in my new GameData.plist file is not appearing.
I presume that's because it doesn't get the data again if the file already exists? but then how do I get the new version of the .plist file? I tried removing the if statement, but I get a runtime error saying the file already exists.
It sounds like you need to setup a version system. But, the only reason to copy a file from the bundle to the documents folder is so you can edit it, so you need to think about how to merge the updated file in the bundle and the users additions in the documents folder.
Once you have a plan for merging the files, then you can change your if statement so it doesn't just check if the file already exists (if it does, you need to delete it before you can replace it) but also considers the version. You may want to store the currently copied version number in user defaults and add a version number to the file in the bundle. You may also want to keep the user modified data in a different file and use that as overrides.
Currently using:
NSString* path = [[NSBundle mainBundle] pathForResource:#"filename"
ofType:#"txt"];
This only works however after I manually add the file into the application bundle.
I can use the documents directory but that's even worse.
After resetting the sim, the file goes away. How do I get it to stay?
How would I write out the file from somewhere to there? Like from a file in source? I don't want to alloc a several megabyte NSString object.
Credit goes to Lyle42 on freenode irc (#iphonedev):
I wasn't aware this field even existed. By adding any file into the copy files build phase (under build phases), they persist across builds.
Then this code:
NSString *_filePath = [[NSBundle mainBundle] pathForResource:#"filename" ofType:#"txt"];
NSLog(#"%#",_filePath);
NSData *_binary = [NSData dataWithContentsOfFile:_filePath];
NSString *_fileContents = [[NSString alloc] initWithData:_binary encoding:NSUTF8StringEncoding];
Works just great. (loads contents of file into an NSString).
You can use this to get your path for the file
NSArray *documentPaths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *path = [documentPaths objectAtIndex:0];
filePath = [path stringByAppendingPathComponent:file_name];
you can get the file from your app bundle using
NSString *pathFromApp = [[[NSBundle mainBundle] resourcePath] stringByAppendingPathComponent:file_name];
I can use the documents directory but that's even worse.
Not really. The documents directory is persistent, and the recommended directory for essential documents created by your application. In addition, it's backed up by iTunes, and can be specified for iCloud backup. The app bundle just can't compete with that!
After resetting the sim, the file goes away. How do I get it to stay?
You can't, once the simulator has been "reset" (and I mean a hard wipe of all data and apps), nothing is saved. In between test builds, however, data copied out of the app bundle should survive unless you're storing things in /tmp. In addition, objects in the bundle are "refreshed" (recopied) in the build phase, meaning your resource isn't wiped out, merely reset to the one specified in your project.
How would I write out the file from somewhere to there? Like from a
file in source? I don't want to alloc a several megabyte NSString
object.
To read the file into memory, it would have to turn into a "several megabyte NSString" eventually, but if you really want to add a little IO to your plans for a massive string object, have a look at NSFileManager.
I want to distribute a populated database with my app. I am trying to implement the SQLitePlugin, but as the page says there is not any documentation for how to do it on iOS. I tried to implement this outdated steps but without any luck. Is there anyone who did this?
I found a solution at least it works for me so here is :
I used and followed the tutorial in the link you mentioned : http://gauravstomar.blogspot.fr/2011/08/prepopulate-sqlite-in-phonegap.html
Be sure to copy and past the good database.db and 000...001.db (the good one are either in /Caches and /Caches/file_0 folders or in /WebKit and /WebKit/file__0, i don't remember so try one then the other if it doesn't work). then i pasted them in my resources' project directory.
I modify the two lines :
NSString *masterPath = [libraryDir stringByAppendingPathComponent:#"WebKit/Databases/"];
NSString *databasePath = [libraryDir stringByAppendingPathComponent:#"WebKit/Databases/file__0/"];
by the following lines :
NSString *masterPath = [libraryDir stringByAppendingPathComponent:#"Caches/"];
NSString *databasePath = [libraryDir stringByAppendingPathComponent:#"Caches/file__0/"];
Remove your application from your simulator/device. And then run.. it works, my database is pre-populated.
I put the database in the Resources folder and placed this code in MainViewController.m.
NSFileManager *fileManager = [NSFileManager defaultManager];
NSError *error;
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
NSString *databasePath = [documentsDirectory stringByAppendingPathComponent:#"this.db"];
if ([fileManager fileExistsAtPath:databasePath] == NO) {
NSString *resourcePath = [[NSBundle mainBundle] pathForResource:#"this" ofType:#"db"];
[fileManager copyItemAtPath:resourcePath toPath:databasePath error:&error];
}
The following comes from a blog post - but as I've been slightly spanked for posting links before I'm going to quote from it. I'll post the URL at the end though and I recommend you go there for a full explanation and some follow up comments. This help comes credit Scott Buckel.
1) Use this plugin: https://github.com/chbrody/Cordova-SQLitePlugin/
2) Copy your sqlite db file to /assets folder of PhoneGap.
3) Then, follow instructions here: http://gauravstomar.blogspot.com/2011/08/prepopulate-sqlite-in-phonegap.html to copy the file to native storage. Change this.copy("Databases.db","/data/data/"+pName+"/app_database/");
3a) Instead of Databases.db, use your database filename.
3b) Instead of app_database, use "databases"
3c) You'll probably want to delete the file from /assets, since it is duplicated and no longer needed. My app was double the size it needed to be.
4) (Not sure if this step is necessary, I'm getting out of the office for the day). Edit SQLitePlugin.java
4a) Lines 176, I edited 1==1 so that if statement is always executed. Line 179 I changed this.setStorage(appPackage, false); to this.setStorage(appPackage, true);
5) You can then use the following command to open the DB and use it as any other PhoneGap database
5a) var db = window.sqlitePlugin.openDatabase("[full_database_name_including_extension]", "1.0", "PhoneGap Demo", 200000);
Full blog post: http://www.raymondcamden.com/index.cfm/2012/7/27/Guest-Blog-Post-Shipping-a-populated-SQLite-DB-with-PhoneGap
I'm trying to load a plist file in my application with
NSBundle* bundle = [NSBundle mainBundle];
NSString* plistPath = [bundle pathForResource:#"CategoryData" ofType:#"plist"];
categoryProps = [[NSArray alloc] initWithContentsOfFile:plistPath];
but the categoryProps array always ends up with 0 objects. I've placed the CategoryData.plist file under the "Supporting Files" dir in my project but I can't figure out how files are arranged in the compiled app.
Can someone point me to docs that describe how the file system of an app is laid out and how to figure out where files are located within the file system?
I forgot to point out that I am using XCode 4 which does not create a resources folder for the project
Your loading code code should work for locating the file within the file system. In a project, I have:
NSString *data = [[NSBundle mainBundle] pathForResource:#"data" ofType:#"plist"];
What I would do is log the plistPath to the console or inspect it in the debugger, then navigate to that location on disk and determine if the plist ends up where you think it does.
Also, locate your application bundle in /Users/<# Username #>/Library/Developer/Xcode/DerivedData/<# Unique Appname #>/Build/Products/Debug-iphonesimulator/<# Appname #>.app, right click on it and select "Show package Contents". Ensure that you see your plist where you think you should.
You need to place your plist file in the Resources folder. Then you will be able to load and use them like this
NSString *path = [[NSBundle mainBundle] bundlePath];
NSString *finalPath = [path stringByAppendingPathComponent:#"Info.plist"];
NSDictionary *plistData = [[NSDictionary dictionaryWithContentsOfFile:finalPath] retain];
UPD: In xcode4 you must to place plist files in the "Supporting Files" directory instead of "Resources". And try to use NSDictionary instead of NSArray