Follow

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use
Contact

Is there a faster way to loop through the installed applications on macOS?

I’m writing a menu bar extra that shows you a list of your installed apps and allows you to click on each button in the list to open that app. Obviously, to do this I need a list of every app the user has. The specific way I chose to do this was making a function that would loop through the files in the system’s Applications folder, strip out anything in an app’s contents or that didn’t end in .app, and return an array containing a list of files as names, which is then iterated through to create a list of "app buttons" that the user can click on to launch the app.

The code for my function is

func enumerateAppsFolder() -> Array<String> {
    var fileNames:Array<String> = []
    
    let fileManager = FileManager.default
    let enumerator:FileManager.DirectoryEnumerator = fileManager.enumerator(atPath:"/Applications/")!
    
    while let element = enumerator.nextObject() as? String {
        if element.hasSuffix("app") && !element.contains("Contents") { // checks the extension
            fileNames.append(element)
        }
    }
    return fileNames
}

And I create my list with

MEDevel.com: Open-source for Healthcare and Education

Collecting and validating open-source software for healthcare, education, enterprise, development, medical imaging, medical records, and digital pathology.

Visit Medevel

ForEach(enumerateAppsFolder(), id:\.self){
    AppBarMenuItem(itemAppName: $0)
}

But when I do it like that, the result is what I expected, but the performance is horrible. This can be seen in the screenshot, and will just be made worse by larger applications folders on some people’s systems
Screenshot of bad app performance
(When the app is starting up, which takes about 5 minutes, the CPU and disk usage are also extremely high)
Is there a better and faster method that will retrieve every app on the system, similarly to the macOS launchpad or "Open With.." list?

>Solution :

The enumerator method of FileManager that you are using performs a deep enumeration of the file tree. You don’t want a deep enumeration, just a top-level enumeration. Use the version of the enumerator method that has the options parameter and pass in .skipsSubdirectoryDescendants.

Here’s an updated version of your function getting a URL directly from FileManager for the Applications folder and then doing a shallow enumeration to get the list of apps.

func enumerateAppsFolder() -> [String] {
    var appNames = [String]()

    let fileManager = FileManager.default
    if let appsURL = fileManager.urls(for: .applicationDirectory, in: .localDomainMask).first {
        if let enumerator = fileManager.enumerator(at: appsURL, includingPropertiesForKeys: nil, options: .skipsSubdirectoryDescendants) {
            while let element = enumerator.nextObject() as? URL {
                if element.pathExtension == "app" { // checks the extension
                    appNames.append(element.deletingPathExtension().lastPathComponent)
                }
            }
        }
    }

    return appNames
}

print(enumerateAppsFolder())

Sample output when run from a Swift Playground:

"Numbers", "Dropbox", "Xcode", "Apple Configurator 2", "iMovie"

Add a comment

Leave a Reply

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use

Discover more from Dev solutions

Subscribe now to keep reading and get access to the full archive.

Continue reading