I've got a PGN file called a00.PGN in my project, which I'd like to parse to 'preload' into Core Data. Right now I'm just trying to output the file as a string with NSLog using
NSError *err = nil;
NSString *datapath = [[NSBundle mainBundle] pathForResource:#"a00" ofType:#"PGN"];
NSString *content = [NSString stringWithContentsOfFile:datapath encoding:NSASCIIStringEncoding error:&err];
NSLog(#"%#", err);
And I'm getting an error: Error Domain=NSCocoaErrorDomain Code=258 "The file name is invalid."
When I NSLog the string content it is NULL.
I have tried changing the extension to .txt but the same error is received, and have also used NSUTF8StringEncoding. What is the best way to read in this file for parsing in objective-c?
Edit 1:
To include the file in my project I dragged it from finder. I then went to my projects target and added it to the Copy Files section with Products Directory as the destination. The Subpath is empty.
Edit 2:
I've uploaded the current state of this simple project to a repo here: https://github.com/justinjdickow/PGN-Parse-into-Core-Data/
According to Technical Q&A QA1436: What is the "main bundle" of a command-line foundation tool? — actually, that document is (partially) wrong.
In a command-line tool executable, the main bundle's path would be the directory that contains the executable itself. For example, in a sample command-line tool project I made here, the following was logged to console:
mainBundle == NSBundle <~/Library/Developer/Xcode/DerivedData/mainBundle-bqmiidcdlnwumjgcnblqgtelpecl/Build/Products/Debug> (loaded),
bundlePath == ~/Library/Developer/Xcode/DerivedData/mainBundle-bqmiidcdlnwumjgcnblqgtelpecl/Build/Products/Debug
In other words, the main bundle will be your built-products directory. For NSBundle's pathForResource:ofType: to be able to find the "A00.PGN" file at runtime, that file will need to be in your built-products directory along with your executable. For example, in my case, I would need the file to be located at ~/Library/Developer/Xcode/DerivedData/mainBundle-bqmiidcdlnwumjgcnblqgtelpecl/Build/Products/Debug/A00.PGN
The easiest way to achieve that is to add a Copy Files build phase to your command line tool target. Specify the Destination as the Products Directory and include the A00.PGN file in the list of files to be copied.
Also, as others have already mentioned, NSBundle's search method is case-sensitive even if the underlying HFS+ file system on OS X is merely case-preserving. So, make sure you specify the correct filename and filename extension in your code.
Note that pathForResource:ofType: has different case-sensitivity depending whether it is iPhone simulator or iPad simulator or the device.
Make sure you are using upper/lower case consistently.
Related
I am developing a mac app and I want to put my settings (array of strings) into a file. If that file exists in the same folder as the app it is read and it overwrites default settings.
No problems writing the file but when reading with code:
NSArray* settingsInput = [[NSArray alloc] initWithContentsOfFile:#"./SettingsFile"];
something strange happens. When running the app from XCode (the settings file is in the build/debug folder next to the app) settings are read without a problem. When I archive the app and run it from the desktop, the file cannot be loaded. Even when I copy the app from the build folder to the desktop it does not work.
What could be the reason for this kind of behaviour? How can I read this file?
It may be a better Idea to use the normal prefence system. NSUserDefaults.
There a couple of ways you can do it.
But the idea is to give your app a set of default preference which are registered for you in the correct domain and always with a fresh app.
Using the registerDefaults: from NSUserDefaults.
See Apples documentation NSUserDefaults and its #registerDefaults
But the one I would use is :
Copy a plist file into the supporting files in you Xcode project.
Making sure "Copy files into destination group's folder" is checked. And the "Add to targets is check also"
The plist file should contain your array of strings.
(I created mine. By duplicating another plist in my user preferences. Renaming it. Copying it to the project. Selecting it and editing it to how I needed it. Making sure I use the file menu ->'Save' to save the changes. )
Declare a NSUserDefaults * prefs;
Now in the - (id)init method for the app. you register the contents of the file as your default preferences.
- (id)init
{
self = [super init];
if (self) {
prefs = [NSUserDefaults standardUserDefaults] ;
NSString *registerDefaultsPlistFile= [[NSBundle mainBundle] pathForResource:#"registerDefaults" ofType:#"plist"];
[prefs registerDefaults:[NSDictionary dictionaryWithContentsOfFile: registerDefaultsPlistFile]];
}
return self;
}
You can later make a call to read these preferences.
NSLog(#" arrayOfStrings = %#", [prefs objectForKey:#"arrayOfStrings" ]);
These default preferences are NOT written to file/out unless you make a change to them. By written to file I mean to the applications preference file. Once you do make a change to them then they will be written out into the users preferences and those are what will be used from then on.
You should not rely on the current directory of the app. You should either read from the app bundle (see NSBundle class for get the correct path) or the app's document directory (see NSSearchPathForDirectoriesInDomains(NSDocumentDirectory ...).
The UNIX concept of the current working directory is not commonly used in Mac desktop applications. When launching an app through the Finder it's usually set to the root directory of the boot volume.
You should find another way to determine locations for your settings files. Good spots would be ~/Library/Preferences or ~/Library/Application Support/MyApp. You can get the absolute path to these directories using:
NSString *path = NSSearchPathForDirectoriesInDomains(NSApplicationSupportDirectory, NSUserDomain, YES)[0];
I am attempting to read a number of files from a directory tree inside the Documents directory within my App andbox.
Most of them are xml files which I read with :
NSData *data = [NSData dataWithContentsOfFile:packagefile.
RXMLElement *rxml = [RXMLElement elementFromXMLData:data];
All but one them read OK. But one does not read and I get error code 256.
For the bad file I can also try :
NSData *data = [NSData dataWithContentsOfFile:packagefile options:0 error:&err];
but the same error code comes back . (Hence I think my issue is not related to the actual xml content of the file).
Clearing out the sandbox and copying all the files back in programatically in my App makes no difference.
What can be wrong here? What else can I do?
I have found a solution to this problem ('inexplicable error code 256'.) Hopefully this solution will be helpful to other developers who hit this issue.
It had run out of file handles.
In my App I had a also logging function which logged activity to file (a bit like Log4J). This took each logged statement – opened a file using a 'NSFileHandle' followed by 'seekToEndOfFile' followed by 'writeData'.
I was relying mistakenly upon the ARC disposing and closing the file on exit from the logging procedure. Clearly ARC does not close the file and eventually the system runs out of file handles.
The solution is simply to call 'closeFile' as one would in Java or C.
I know .xib files are really .xml files (as evidenced primarily by the fact that I can rename their extension and open them in text edit and dashcode), so theoretically, it would be possible to open a xib as xml and parse it in an application. I took a look in the Build Settings for my sample app, and found that xib's aren't compiled, rather they are Copy bundle Resources. Sounds good, right? But the files are giving me (null) strings when I try and get their text with these methods:
NSString *string = [[NSBundle mainBundle]pathForResource:#"Empty" ofType:#"xib"];
NSString *xmlString = [[NSString stringWithContentsOfFile:string encoding:NSUTF8StringEncoding error:nil] retain];
NSLog(#"%#", xmlString);
[textView setText:xmlString];
It NSLog's (null) both for xmlString and for string. So I thought of trying it with another Copy Bundle Resource, so I changed string to this:
NSString *string = [[NSBundle mainBundle]pathForResource:#"InfoPlist" ofType:#"strings"];
and it logged a perfect file path! However, xmlString still logged (null).
Then, I tried getting an error code, and I got this:
The operation couldn’t be completed. (Cocoa error 258.)
It makes me think there is some extra compiling going on behind the scenes with .xib files (I read somewhere that they are turned into nibs by the compiler) that is making them unreadable or unreachable. But there are apps out that I know of (Interface being a prime example) that can read and write .xib files.
So, can anyone help me get the xml of a .xib file?
Actually xib files are compiled into nib files. Use Show Package Contents on the compiled .app and you will see that there are no xib files but nib files instead.
Edit It looks like the nib file format is a proprietary format. But I am not sure.
Form Apple documentation:
The original format uses files with extension .nib. This a proprietary, binary format for the representation of Interface Builder user interface objects and connections.
Edit 2 It looks like there is some option to prevent Xcode from compiling the xib files into nib files. But I am still not sure about how to do that, and what is the actual result.
Edit 3 I tried that on Xcode. When I turned Flatten Complied Xib files to No, I still got nib files in the binary but they are not compressed this time. Inside these nib files there is a file designtime.nib that has an xml format.
I have a problem accessing my files in my app.
I am currently using
//Directly from TileMap example from WWDC2010
NSString *tileDirectory = [[[NSBundle mainBundle] resourcePath]
stringByAppendingPathComponent:#"Tiles"];
to access my tiles for my MKOverlay. This gives me this directory
/Users/xxxx/Library/Application Support/iPhone Simulator/4.2/Applications/9D62025C-C53B-472C-8309-xxxx/xxxx.app/Tiles
The x's is only for privacy reasons
I have my tiles in a folder called Tiles in the root of my application which is in Xcode in a group called Tiles which is in directly in the Resources group.
When I run my app, I get a simple error saying that it could not find my tiles at the generated directory (the one quote above) If I replace that piece of code and make it:
NSString *tileDirectory = #"/Users/xxxx/Documents/xxxx/Tiles";
Then my app works fine. This is obviously because it finds my tiles in its direct location on my Mac. This is fine for testing, but I need it to work on my iPhone/iPad.
This problem might be occurring due to:
The generated directory is incorrect.
The tile images aren't getting included in the builded .app file.
Either way, I have no clue of what to do to solve it.
How can I solve this problem?
[EDIT]
I changed that piece of code to:
NSString *tileDirectory = [[NSBundle mainBundle] resourcePath];
Now it works in simulator, because all files are in the apps root folder and I don't ask for it to enter another directory called "Tiles".
This runs with no error on the simulator, but when on my iPhone it gives the original error (just a different file path but also ending with /xxxx.app
How can I ensure a directory in my app file such as xxxx.app/Tiles - TileMap does this.
Since it is your files in your app bundle, I think you can use pathForResource:ofType: to get the full pathname of your file.
Here is an example:
NSString* filePath = [[NSBundle mainBundle] pathForResource:#"your_file_name"
ofType:#"the_file_extension"];
Remember that the "folders/groups" you make in xcode, those which are yellowish are not reflected as real folders in your iPhone app. They are just there to structure your XCode project. You can nest as many yellow group as you want and they still only serve the purpose of organizing code in XCode.
EDIT
Make a folder outside of XCode then drag it over, and select "Create folder references for any added folders" instead of "Create groups for any added folders" in the popup.
If your tiles are not in your bundle, either copied from the bundle or downloaded from the internet you can get the directory like this
NSString *documentdir = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) lastObject];
NSString *tileDirectory = [documentdir stringByAppendingPathComponent:#"xxxx/Tiles"];
NSLog(#"Tile Directory: %#", tileDirectory);
You need to use the URL for the link, such as this:
NSURL *path = [[NSBundle mainBundle] URLForResource:#"imagename" withExtension:#"jpg"];
It will give you a proper URL ref.
You need to add your tiles into your resource bundle. I mean add all those files to your project make sure to copy all files to project directory option checked.
I'm creating a simple application with xcode and objc and I need to load an NSDictionary from a file, but I can't get the path to the file using NSBundle:
NSString *l = [[NSBundle mainBundle] pathForResource:#"LoginStatuses" ofType:#"plist"];
NSLog(#"%#", l);
When I run this code I get this:
2010-10-16 10:42:42.42 Sample[5226:a0f] (null)
And I don't know why.
I created a group called Resources and there I added the LogingStatuses.plist:
So here's the solution for this problem after I got the source:
I didn't really pay attention to the posted screenshot, but the target is of type "Command-line Tool"... and since those don't have a bundle [NSBundle mainBundle] of course returns nil. It's pretty misleading that Xcode doesn't complain that it can't execute the "Copy Bundle Resources" step, it just silently skips it.
Solution is simply to add a new target, of type "Application" so a bundle-based application is generated. Then check the Target Membership checkboxes for all sources and resources for this new target. The plist paths are correctly resolved then.
I was trying to get my iPhone app to use a default sqlite database and the darn app couldn't find it. Turned out that I had to make sure that the .sqlite file was in the bundle resource.
Select your project
Select Target
Select Build Phases tab
Open the section labelled "Copy Bundle Resources"
Drag and drop your .sqlite file into this section.
now your app will find this default sqlite database.
Is the file really included in the target (and will therefor be copied to the bundle) ? There two ways to find out/set that:
First way: right-click (or Cmd-click) on the file, select "Get Info". Then click on the "Targets" tab and make sure the file is checked for the desired target(s).
Second way: right-click (or Cmd-clock) in the project browser on the header of the file browser (it will likely read "Groups & Files"). Then select "Target Membership". Now you have checkboxes next to each file that can be member of a target (like .m files or resources). Again, make sure the checkbox next to your file is checked.
Since I have googled here, did not find the answer, but then discovered it by myself, I'll leave it here...
I had 2 files: tray.png and tray#2x.png for Retina. The files were added to "Copy Bundle Resources" automatically.
But:
[[NSBundle mainBundle] pathForResource:#"tray" ofType:#"png"];
did not return the file actually copied to the bundle! The reason was: IDE created one TIFF file tray.tiff (joint tray.png and tray#2x.png), so ... ofType:#"tiff"] helped!
My problem and solution are very similar to Dmitriy Isaev's ones. I have tried to get path for a xib file. Both calls (in Swift) pathForResource("myfile", ofType: "xib") and pathForResource("myfile.xib", ofType: nil) are failed.
The solution is to use extension "nib" instead:
pathForResource("myfile", ofType: "nib")
I encountered this issue today with a Command Line project.
Luckily, the solution is easy. Simply go to "Build Phases", click on "+" and add a new "Copy Files" phase. Choose "Resources" as Destination, and add any files you want to use.
Now you can still use [NSBundle mainBundle] and it should work!
In my case (executing XCTestCase) for some reason resources were stored in non-main Bundle. I fixed the problem by checking first which bundle test class belongs to:
[[NSBundle bundleForClass:[self class]] pathForResource:#"Config" ofType:#"plist"];
Hopefully this can help someone else as well.
Filename is case sensitive on iPad. You need use small letters.
There is a way to do this for a Command-Line app.
Instead of using "Copy Bundle Resources" use "Copy Files". For "Destination" select "Resources". This will copy the file to the app bundle and the Bundle.main can find it.
Make sure you spell your resource's file name properly. I just learned that the hard way. :)