Path to Resources folder in plugin on OS X - xcode

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.

Related

Xamarin ArcGISRuntime .net System.IO.FileLoadException: 'File exception: The supplied file path contains directories that do not exist.'

https://github.com/Esri/arcgis-runtime-samples-dotnet samples
I am exploring the xamarin samples that are available through the link above. One sample that is called Generate geodatabase here shows how to generate a geodatabase and store it locally on the device.
Problem:
I am running the sample for UWP app and it breaks showing this.
System.IO.FileLoadException: 'File exception: The supplied file path contains directories that do not exist.'
I have investigated the issue and here are my findings:
The file path "_gdbPath" supplied to
_generateGdbJob = _gdbSyncTask.GenerateGeodatabase(generateParams, _gdbPath);
is obtained using the following method:
private string GetGdbPath()
{
// Set the platform-specific path for storing the geodatabase
String folder = "";
#if NETFX_CORE //UWP
folder = Windows.Storage.ApplicationData.Current.LocalFolder.Path.ToString();
#elif __IOS__
folder = Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments);
#elif __ANDROID__
folder = Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments);
#endif
// Set the final path
return Path.Combine(folder, "wildfire.geodatabase");
}
Now, the GbdPath location is on the devise and it is correct. I physically went opening the path and also I have tried creating a file in the path/folder like the following:
async void WriteTimestamp()
{
Windows.Globalization.DateTimeFormatting.DateTimeFormatter formatter =
new Windows.Globalization.DateTimeFormatting.DateTimeFormatter("longtime");
StorageFile sampleFile = await localFolder.CreateFileAsync("wildfire.geodatabase",
CreationCollisionOption.ReplaceExisting);
await FileIO.WriteTextAsync(sampleFile, formatter.Format(DateTime.Now));
}
It worked and the file was successfully created. So, the file system is accessible.
Also, I tried it on Android with a little modification/change, I made it work here the change.
Question:
What's wrong with UWP platform in my case (I have not tested IOS)?
Update:
I have done further research and found that there might be a bug (that I will report) in GeodatabaseSyncTask class GenerateGeodatabase method. It cannot find the LocalState folder in case of Windows 10 configured to store new content in another location (other than on default c: drive) like in my case
see here . So, if windows 10 is configured to store app content on another drive (like in my case) the class mentioned above cannot see it (based on observation only, I did not decompile it). Windows 10 stores the content on another drive in WpSystem... folder. System will create a shortcut called LocalState junction, if you explore the default location using command prompt cmd, that point to the custom location.
I do not know what would be the best work around till it is fixed.

Quickly find the full path to an app whose name contains a specific phrase

I'm working on a little app that acts as a plugin for a 3rd party app. For it to function, I need to access a saved file from the 3rd party app ("other app" from here). In newer versions, the other app keeps this in ~/Application Support/Test App/Saved Files/.
This is easy to go to, using something like this:
NSString *savedFiles = [NSHomeDirectory() stringByAppendingPathComponent:#"~/Application Support/Test App/Saved Files"];
But I also want to make it compatible with older versions, and they don't use the application support folder. Older versions went in their folder in the user's applications folder, for example this structure:
~/Applications/Test App/Test App.app
~/Applications/Test App/Saved Files/
I can use the same as above to find the location to saved files. But the app won't always be installed here, or the folder name could be different in a different language. This is the problem.
I thought so far, I thought the following are options to get the path:
Ask the user to navigate to the file with a file selector from
within my app.
Ask the user where the "Test App.app" is, and get the
path via "../Saved Files/" relative to that.
Can the app get the path to a previously run application via the system?
Can the app use spotlight to quickly find the location?
Are there other ways to do this automatically, reliably, and quickly?
1 and 2 are not preferred, as that would require the user to "work" to give the app the path. What do you all think?
Are there other ways to automate this?
Which would be the fastest?
You can try to ask LaunchServices for the app's location, given its bundle identifier with the LSFindApplicationForInfo call:
CFURLRef urlRef = NULL;
LSFindApplicationForInfo(kLSUnknownCreator, (CFStringRef)#"com.example.some-app", NULL, NULL, &urlRef);
NSLog(#"URL: %#", (__bridge id)urlRef);
This assumes that the bundle identifier is the same between the two variants you mention. If it's really just the name that is the same, you could query by name instead:
CFURLRef urlRef = NULL;
LSFindApplicationForInfo(kLSUnknownCreator, NULL, (CFStringRef)#"Test App.app", NULL, &urlRef);
NSLog(#"URL: %#", (__bridge id)urlRef);

SDL with Xcode requires absolute paths

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!

Mac App Store - can't save a file

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.

Mac app default working directory

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,

Resources