I've made many apps in Xcode before and always their default working directory was the one where the .app file is, so I've accessed the internal data like Whatever.app/Contents/Resources. I know it's probably not the right way, but it has always worked. Anyway, either from a recent Xcode update or for some other reason, their default working folder is now being set to "/". This only happens when I run the .app file from Finder. If I run it from within Xcode, the folder path is correct (I can set that path in the executable options, but it has no effect on what happens when you run the .app directly). Is this a setting somewhere or just the new standard?
For resources, use this cool code:
NSString *path = [[NSBundle mainBundle] pathForResource:#"awesomepic" ofType:#"png"];
You should never depend on PWD with Cocoa. Instead, use Cocoa API's whenever possible. BSD API's should only be used if Apple provided no other way.
globheader.h
static char *appdir;
appcontroller.m
#import "globheader.h"
#implementation AppController
- (void)method {
appdir = [[[NSBundle mainBundle] bundlePath] UTF8String];
}
#end
cppcode.cpp
#include "globheader.h"
int main() {
printf("%s", appdir);
return 0;
}
toastie, why use the application bundle to store data? don't change your .app.
Instread, use the application support folder. You can modify the bytes in that folder with no problems. Read the following post from Matt Gallagher,
http://cocoawithlove.com/2010/05/finding-or-creating-application-support.html
regards,
Related
Question
Is it possible to determine the location a macOS app was launched from at runtime?
Context
I develop a Safari Extension, and in order for the extension to be enabled the application needs to be present /Applications/. Several users have tried to run the application directly from the DMG file and then complained that the extension doesn't appear in Safari's extension settings. I would like to detect this and alert them that they need to move the file.
You can use NSBundle, specifically the bundlePath property. The documentation says:
The full pathname of the receiver’s bundle directory.
And you can use it something like this:
NSString* bundlePath;
bundlePath = [[NSBundle mainBundle] bundlePath]
NSLog(#"%#", bundlePath);
There's also bundleURL if you want a NSURL to work with instead of a string.
I'm in the process of learning how to use xcode and how to write applications for the Mac.
So, I have a simple screensaver that loads an external file which works fine. I decided that it would be better to embed the html source in the application to reduce the external dependancies.
I dragged the HTML file to the resources folder, it's at the top level there's no sub folder or anything.
I made sure the Add to targets had my application in it, along with selecting 'create folder references' and 'copy files if needed'. I use the following to get the url to the file, but it returns nil
NSURL *fileUrl = [[NSBundle mainBundle] URLForResource:#"embedded" withExtension:#"html"];
System Preferences[10185]: fileUrl returned (null)
I've read through all the other SO issues for the similar problems but none of the fixes seem to apply. I've gone through and checked them.
If I show the package contents of the final build the embedded.html is in there. Am I missing something really obvious?
edit to add
In build phases it's listed in Copy Bundle Resources
xcode version is 6.1.1
After some considerable digging and a smidgeon of luck I found that
NSURL *fileUrl = [[NSBundle mainBundle] URLForResource:#"embedded" withExtension:#"html"];
is incorrect and I should be using
NSURL *fileUrl = [[NSBundle bundleForClass:[self class]] URLForResource:#"embedded" withExtension:#"html"];
instead.
i.e. NSBundle bundleForClass:[self class] and not NSBundle mainBundle will give access to a screensaver bundle.
Might be obvious, but only if you know ;)
See Apple Developer NSBundle Class Reference
I had a similar problem. The answer for me was to check the Target Membership of object's identity.
Quitting and restarting Xcode fixed this for me.
using this solved my problem [myBundle URLForResource: withExtension: subdirectory: ]
Subdirectory queries to NSBundle URLForResource need to specify the subdirectory. Otherwise it returns nil.
One hour figuring this today :'(
I met a same issue in a MAC application. After several hours, I found an easy way to fix it.
1, Make sure there is a phase named "Copy Files" under "Build Phases". If not, please add one.
2, Add your resources into the phase.
3, Rebuild your project and you can find your resources located in ~/Library/Developer/Xcode/DerivedData//Build/Products/Debug/.
I'm trying to make an SDL application in Xcode, but I'm having trouble loading images. I'm using this template, because I couldn't get it to work when made from scratch.
Whenever I try to load an image with SDL_LoadBMP however, it returns NULL unless I give the absolute path. (/Users/Cole/code...) I looked in the exported .app file, and it does have the image I want to load in Contents/Resources/, and I've tried every combination I can think of to get at those (../Resources/image.bmp, ect.) but I can't seem to get it working.
Does anyone have a solution? I'm running Mac OS 10.7 with Xcode 4, so I can't use the templates that is within the SDL download.
Also, I tried using SDL_ttf, but I get this error:
warning: Unable to read symbols for #executable_path/../Frameworks/SDL_ttf.framework/Versions/A/SDL_ttf (file not found).
warning: Unable to read symbols from "SDL_ttf" (not yet mapped into memory).
There does not happen to be a Frameworks folder where it's looking, but somehow it finds the regular SDL framework just fine.
You can get the path to your the Resources directory containing your file with
NSString *path = [[NSBundle mainBundle] resourcePath];
or alternatively (in theory more clean as it can access localized files) you can get the full file name with
NSString *file = [[NSBundle mainBundle] pathForResource:#"image.bmp" ofType:nil];
You'll need to pass the C string to SDL_LoadBMP, so either of the two:
SDL_LoadBMP([[path stringByAppendingString: #"/image.bmp"] UTF8String]);
SDL_LoadBMP([file UTF8String]);
I had the same problem and found a way without using any objective-c.
In xcode click on your target then go onto the build phase section
Then in the top bar click: Editor -> Add Build Phase -> Add Copy Files Build Phase
Now change the destination of the newly created phase to "Products Directory" and then add any subpaths if needed.
All you need to do now is add your image onto the list below and it should work!
Are there any restrictions as far as saving files when you distribute an app over the Mac App Store?
If I compile and run my app from my computer it works fine - it saves the configuration.
However the version that was downloaded over the Mac App Store is not saving the configuration. I don't even see the config file. Anyone knows what is going on?
This is the code that saves the config:
-(void)saveConfig:(id)plist {
NSString *path = [[[NSBundle mainBundle] resourcePath] stringByAppendingString: CONFIG_FILE_NAME];
NSData *xmlData;
NSString *error;
xmlData = [NSPropertyListSerialization dataFromPropertyList: plist
format: NSPropertyListXMLFormat_v1_0
errorDescription: &error];
if(xmlData)
{
if (![xmlData writeToFile:path atomically:YES])
NSLog(#"Failed to write the config file onto the hard drive");
}
else
{
NSLog(error);
}
}
You cannot write files to the application bundle directory if you’re targeting the Mac App Store. The bundle is supposed to be immutable.
Consider saving your configuration with NSUserDefaults or, if you truly need a separate file, the officially recommended location is (~)/Library/Application Support. Matt Gallagher wrote a nice post called Finding or creating the application support directory in which he provides a solution that uses standard NSApplicationSupportDirectory followed by the executable name.
Generally, you should assume that your application's assets are read-only. This is true in general, not just for the app store.
If you want to save user settings as a property list, use NSUserDefaults instead of modifying files inside the application. This will "just do the right thing", which is to save preferences to ~/Library/Preferences.
I'm writing a FileMaker Pro plugin for OS X, and I need to be able to access a file in the /Applications/FileMaker Pro 11/Extensions/blah.fmplugin/Contents/Resources directory. (But this could be /apps/FileMaker Pro 11 Advanced/Extensions/xyz.fmplugin/Contents/Resources or something else, depending on where the user installed FMP and whether they renamed the plugin etc.) Since I can't control where the user has installed FMP, or whether they are running FMP or FMPA or whether they have renamed my plugin (although this is less likely than the FMP vs FMPA situation), I do not know the path to the plugin's bundle.
I've found answers like this: Relative Paths Not Working in Xcode C++ but that gives me the path to the Resources folder in the FileMaker.app instead of in my plugin.
i.e. I get /Applications/FileMaker Pro 11/FileMaker Pro.app/Contents/Resources instead of the path to the Resources folder in my plugin.
Is there something I need to configure in Xcode to get the above solution to work? Or is there some other way to get at the path? The CFBundleGetMainBundle() call makes it sound like it is meant to return the bundle of the app itself rather than the plugin.
I have found some possibilities based on the Bundle Programming Guide and CFBundleGetBundleWithIdentifier (mentioned by mipadi), but I haven't been able to figure out yet exactly what functions I need and what the prerequisites are.
This seems to do it. CFBundleGetBundleWithIdentifier should have worked too, but I decided to go with the NSBundle stuff rather than CF*. I put this in a .mm file and included it in the project. I can then call the get_resources_path() function and get the path as a string.
#include <Foundation/Foundation.h>
#include <string>
std::string *get_resources_path()
{
NSBundle *bundle;
bundle = [NSBundle bundleWithIdentifier: #"com.example.fmplugin"];
if (bundle == NULL) {
// error handling
}
NSString *respath = [bundle resourcePath];
if (respath == NULL) {
// error handling
}
const char *path = [respath UTF8String];
if (path == NULL) {
// error handling
}
return new std::string(path);
}
You can use CFBundleCreate if you know the path to your bundle; or you can get the bundle using the bundle identifier via CFBundleGetBundleWithIdentifier.