NSWorkspace 有 https://developer.apple.com/documentation/appkit/nsworkspace/1534059-runningapplications,它返回 NSRunningApplication 的列表。
NSRunningApplication
提供了icon
、bundleURL
、executableURL
等,以及terminate()
等方法。
我正在为其他已安装但未运行的应用程序寻找同样的东西。
我知道
system_profiler SPApplicationsDataType
返回应用程序列表,但它没有提供我需要的信息。
我花了几年时间专业地解决这个问题,其中包括与 Apple 合作的几个 DTS。没有全面、可靠的解决方案。甚至没有一个完全适用的“应用程序”定义或“安装”的含义。有很多启发式方法。在 Mac App Store 出现之前,Apple 一直以拖放安装而自豪,因此没有什么比 Windows 添加/删除程序系统更好的了。程序是通过现有程序“安装”的。
查看
/Applications
和 ~/Applications
是一个良好的开始。您可能还会发现以下有用:
/System/Library/Frameworks/CoreServices.framework/Versions/A/Frameworks/LaunchServices.framework/Versions/A/Support/lsregister -dump
pkgutil --pkgs
mdfind "kMDItemKind == 'Application'"
最后一个是一个很棒的工具,因为它的速度非常快。如果 Spotlight 数据库尚未更新(例如在克隆机器后立即更新),它也有点不可靠。 Spotlight 知道第一次扫描何时完成,因为在此之前它会在其图标中放置一个小点来告诉您它尚未准备好。没有 API 可以请求该状态(至少在我处理这个问题时没有)。
一旦有了路径,您就可以使用
mdls
之类的东西来查询其他信息。
请注意,Apple 多年来改变了很多事情,因此如果您需要支持很多操作系统版本,请务必在这些操作系统版本上进行测试。苹果一直在让其中一些东西更像是一个黑匣子,而不是更少。像
LSCopyApplicationURLsForBundleIdentifier
这样的工具现已弃用,我不知道有任何替代品。
一般来说,你的问题是找到太多东西而不是太少。您可能会对系统上实际有多少“应用程序”感到惊讶。我有近600个,包括:
/Library/Application Support/Script Editor/Templates/Droplets/Recursive File Processing Droplet.app
/Library/Application Support/Script Editor/Templates/Droplets/Droplet with Settable Properties.app
/Library/Application Support/Script Editor/Templates/Droplets/Recursive Image File Processing Droplet.app
/Library/Application Support/Script Editor/Templates/Cocoa-AppleScript Applet.app
还有:
/Users/rnapier/Library/Application Support/VisualStudio/8.0/LocalInstall/Installer.Backup/11-8.10.4.11/Visual Studio Update.app
您可能遇到的另一个非常困难的问题是没有层次结构。应用程序就是应用程序。根据我的经验,这个领域的问题通常想要注册“Microsoft Office”已安装,但没有名为“Microsoft Office”的“应用程序”。这是通过安装各种其他应用程序来暗示的。这绝对是你必须自己编译的东西。当我在这个领域工作时,其中很大一部分价值在于拥有多年来积累的庞大数据库,其中记录了人类已知的每个应用程序的每个版本如何映射到各种工件,以便可以以有用的形式显示。了解所有这些细节和数以千计的例外情况是系统管理系统的真正核心。
您可以枚举 /Applications 目录、/System/Applications 目录、~/Applications 目录或您认为“已安装”应用程序所在的任何其他位置中的文件。
guard let enumerator = FileManager.default.enumerator(
at: URL(filePath: "/Applications"),
includingPropertiesForKeys: nil,
options: [.skipsPackageDescendants]
) else { return }
for case let url as URL in enumerator {
// ...
}
假设所有“应用程序”都有一个捆绑包(简单的可执行文件没有),您可以尝试从中创建一个
Bundle
并获取您需要的信息。
guard let bundle = Bundle(url: url) else { continue }
print(bundle.executableURL)
print(bundle.bundleIdentifier)
print(bundle.infoDictionary)
使用
infoDictionary
访问其 Info.plist 文件中的密钥。请参阅此处可用的按键。
要获取图标,您可以执行以下操作:
if let dict = bundle.infoDictionary,
let iconName = dict["CFBundleIconFile" /* or CFBundleIconName for newer versions*/] as? String,
let url = bundle.urlForImageResource(iconName) {
NSImage(contentsOf: url) // or whatever else you want...
}