I have an application I want to bundle (with some additional files) together inside a wrapper application.
Literally the only thing I want to do is have the wrapper application launch a resource file (i.e. as if I'd typed:
/path/to/Resources/Bundled.app/Contents/MacOS/executable
into the terminal.
Make sense? I thought this should be simple, but I caouldn't find a simple way to do this-- my cocoa programming is limited, to say the least.
Thanks in advance!
One way, if the wrapped “application” is just a shell script or something, is Platypus.
If it's an actual application (.app bundle), why does app A do nothing but launch app B? Why have app A at all?
Your outer program can use NSBundle to locate the inner program within the outer program's bundle.
To run the inner program: If it's an application, use Launch Services or NSWorkspace; if it's a command-line tool, use NSTask.
I have a blog post up on this: Turn any shell script into a double-clickable app. The entry mentions "start with an empty app bundle"... which you can get by using the Pashua tool mentioned, if I remember correctly...
Just for the sake of posterity (and if it helps anyone else, here is the full code I used (inside the AppDelegate.m file):
NSString *appName = #"";
NSString *bundledApp = [[NSBundle bundleWithPath:[[NSBundle
mainBundle] pathForResource:appName ofType:#"app"]]
bundlePath];
NSWorkspace *launchApp = [[NSWorkspace alloc] init];
NSLog(#"Launching %s", bundledApp);
[launchApp launchApplication:bundledApp];
[launchApp release];
// Make Launcher terminate (if it serves no other purpose)
[NSApp terminate:nil];
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/.
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 have a port of my application, a game, running on Mac OS X. I build with make, and only added a few .mm files to access the necessities from NSApplication, NSWindow and NSOpenGLView.
How do I "convert it" into a proper App as opposed to the current console form? Do I bundle it with something, if so how? Or is it some type of linker setting (as in Windows)? Do I have to build using XCode?
Currently I just "tick" the system, i.e. poll it for events rather than utilizing OS X message pump properly. Could you point me to some basic sample or tutorial which shows me how to do it properly, while still being able to "tick" my own stuff at a fixed frame rate? I say "sample or tutorial", since I am blind when it comes to documentation.
Thanks!
For the creation of a proper Mac application, you'll have to create a Mac OS X bundle. You can find the great documentation on the Apple site, here. You can create them with standard tool (I did write a small python script to create a bundle from a simple .ini file that describe files to pack, and how to construct the Info.plist file).
Basically, an application bundle is just a regular directory with a .app extension, and a fixed structure. The following file are required:
Application.app/
+ Contents/
+ MacOS/
| + <Executable>
+ Resources/
| + <Icon>
+ Info.plist
The Info.plist file is a standard property list file (either in XML or in the old format), that indicate what is the name of the executable file (CFBundleExecutable), what is the name of the icon file (CFBundleIconFile), the bundle type (CFBundleType with a value of APPL), and some other informations (file type supported, version string, development language, ...). Those file is the strict minimum required to have a basic Mac OS X application.
For explanation of how the Mac OS X message pump work, I recommend the reading of this article by Matt Gallagher. He explains how the run message of the NSApplication class is implemented. You can then write this method runOnce that only iterate when there are pending messages. You'll then call this function periodically (it is really similar to the PeekMessage, TranslateMessage, and DispatchMessage sequence on Win32):
- (void)runOnce
{
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
[self finishLaunching];
for (;;)
{
[pool release];
pool = [[NSAutoreleasePool alloc] init];
NSEvent *event =
[self
nextEventMatchingMask:NSAnyEventMask
untilDate:nil
inMode:NSDefaultRunLoopMode
dequeue:YES];
if (sender == nil)
break;
[self sendEvent:event];
[self updateWindows];
}
[pool release];
}
Unless you really want to learn a bunch of Mac-specific APIs and XCode, you should just use SDL. It's a cross-platform library that abstracts things like event handling, threading, and multimedia. It includes XCode templates that allow you to build a proper app bundle with minimal changes to your code. Using SDL would mean throwing away most of the Mac-specific code you've written, but it would vastly simplify any future ports and would make it easier to maintain your app for multiple platforms.
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,