I am writing an application which stores data about other applications. I can find the current front most application and its information easily enough:
let frontApp = NSWorkspace.shared.frontmostApplication!
and from there I can get:
let currentAppName = currentApp.localizedName ?? "Dunno"
let currentAppIcon = currentApp.icon
If the application is not in the foreground, I know I can iterate through other running applications:
let apps = NSWorkspace.shared.runningApplications
for app in apps as [NSRunningApplication] {
print(app.localizedName ?? "whatever")
print(app.bundleIdentifier ?? "bundle")
}
I would like to get the same information about a application which is not currently running, but for which I do have the bundle identifier. How can I get this?
First, get the application's bundle using
let bundleURL = NSWorkspace.shared.urlForApplication(withBundleIdentifier: someBundleID)!
let bundle = Bundle(url: bundleURL)!
Once you have the bundle, you can get most of the things that you can get from a NSRunningApplication
:
bundleIdentifier
you already know.bundleURL
you just got with the code above.executableArchitecture
is now bundle.executableArchitectures
, with a plural. Because it's not running, you get all the architectures that it can be run on, rather than the specific architecture that it is running on.executableURL
you can get with bundle.executableURL
, no problems there.launchDate
, isFinishedLaunching
, processIdentifier
, ownsMenuBar
are nonsensical to get, since the application is not running.Now we are left with icon
and localizedName
. The methods I propose for getting these are less reliable. To get icon
, do
NSWorkspace.shared.icon(forFile: bundleURL.path)
I'm sure this produces the same result as NSRunningApplication.icon
at least 99% of the time, but I'm not sure if there are edge cases...
To get localizedName
is more unreliable. There are 2 ways, neither of which are good. Choose the one that fits your purpose the best
FileManager.default.displayName(atPath: bundleURL.path)
This gives you the localised name, but if the user renames the application, it will return the new name, rather than the bundle name
Getting CFBundleDisplayName
from the bundle's localizedInfoDictionary
, falling back on CFBundleName
and infoDictionary
. This for some reason doesn't give the localised name, even though I said localizedInfoDictionary
.
let infoDict = (bundle.localizedInfoDictionary ?? bundle.infoDictionary)
let localizedName = infoDict?["CFBundleDisplayName"] ?? infoDict?["CFBundleName"]