Normally an application bundle on OS X can only be started once, however by simply copying the bundle the same application can be launched twice. What\'s the best strategy t
There's a mysterious Info.plist key called "Application prohibits multiple instances," but it doesn't seem to work for me. I am writing a CLI application and executing it from within a bundle. Perhaps it would work in a GUI application, but I haven't tried.
detect if application with same bundleID is running, activate it and close what starts.
- (id)init method of < NSApplicationDelegate >
NSArray *apps = [NSRunningApplication runningApplicationsWithBundleIdentifier:[[NSBundle mainBundle] bundleIdentifier]];
if ([apps count] > 1)
{
NSRunningApplication *curApp = [NSRunningApplication currentApplication];
for (NSRunningApplication *app in apps)
{
if(app != curApp)
{
[app activateWithOptions:NSApplicationActivateAllWindows|NSApplicationActivateIgnoringOtherApps];
break;
}
}
[NSApp terminate:nil];
return nil;
}
This is extremely easy in Snow Leopard:
- (void)deduplicateRunningInstances {
if ([[NSRunningApplication runningApplicationsWithBundleIdentifier:[[NSBundle mainBundle] bundleIdentifier]] count] > 1) {
[[NSAlert alertWithMessageText:[NSString stringWithFormat:@"Another copy of %@ is already running.", [[NSBundle mainBundle] objectForInfoDictionaryKey:(NSString *)kCFBundleNameKey]]
defaultButton:nil alternateButton:nil otherButton:nil informativeTextWithFormat:@"This copy will now quit."] runModal];
[NSApp terminate:nil];
}
}
See http://blog.jseibert.com/post/1167439217/deduplicating-running-instances-or-how-to-detect-if for more information.
As has already been mentioned Cocoa applications do not usually allow you to run more than one instance at a time.
In general, a cocoa way to solve this look at launchedApplications in NSWorkspace. This returns an NSArray containing a dictionary for each launched application. You can loop through the array to see if the app you are looking for is already running. I would advise that you use the value with the key NSApplicationBundleIdentifier which will have a value like "com.mycompany.myapp" rather than looking for the name. If you need to find the bundle identifier for an app you can look at its info.plist file in the app package.
A low-level solution is to use flock().
Each instance would attempt to lock a file on startup, and if the lock fails then another instance is already running. Flocks are automagically released when your program exits, so no worries about stale locks.
Note that whatever solution you choose, you need to make a conscious decision about what it means to have "multiple instances". Specifically, if multiple users are running your app at the same time, is that ok?
This is a version of seb's for Swift 3.0: If another instance of the app with the same bundle ID is already running, show an alert, activate the other instance and quit the duplicate instance.
func applicationDidFinishLaunching(aNotification: NSNotification) {
/* Check if another instance of this app is running. */
let bundleID = Bundle.main.bundleIdentifier!
if NSRunningApplication.runningApplications(withBundleIdentifier: bundleID).count > 1 {
/* Show alert. */
let alert = NSAlert()
alert.addButton(withTitle: "OK")
let appName = Bundle.main.object(forInfoDictionaryKey: kCFBundleNameKey as String) as! String
alert.messageText = "Another copy of \(appName) is already running."
alert.informativeText = "This copy will now quit."
alert.alertStyle = NSAlert.Style.critical
alert.runModal()
/* Activate the other instance and terminate this instance. */
let apps = NSRunningApplication.runningApplications(withBundleIdentifier: bundleID)
for app in apps {
if app != NSRunningApplication.current {
app.activate(options: [.activateAllWindows, .activateIgnoringOtherApps])
break
}
}
NSApp.terminate(nil)
}
/* ... */
}