Skip to content

FB16362713: NSWorkspace.shared.urlsForApplications(toOpen: .html) is missing some apps #602

@sindresorhus

Description

@sindresorhus
  • Date: 2025-01-20
  • Resolution: Open
  • Area: AppKit
  • OS: macOS 15.2
  • Type: Incorrect/Unexpected Behavior

Description

For example, the Zen Browser app (https://zen-browser.app) is able (registered to handle) to open HTML documents, but does not show up in the list. Same with Firefox. However, they do show up with LSCopyAllRoleHandlersForContentType(kUTTypeHTML, .viewer)?.takeRetainedValue() as? [String] ?? []. The former is documented as a replacement for the latter, but it seems they use different logic. I initially thought there was something wrong with my system, but I also got a report from a user on one of my apps that uses NSWorkspace.shared.urlsForApplications(toOpen: .html) that said Zen Browser was missing, so there is definitely something going on.

I have tried both in a sanboxed and unsandboxed app.

The missing apps live in /Applications, but same result if they are in ~/Applications. I have also tried turning Spotlight off and on again. And I have tried resetting lsregister. Restarting computer. Nothing helped.

The "default browser" setting in System Settings does include Zen Browser, so I have a feeling it is using the older API.

NSWorkspace.shared.urlsForApplications(toOpen: "https:") also does not return Zen Browser, even though the app registers to handle https and http schemes.


I have written a simple script to compare the output of these methods:

func compareHTMLHandlers() {
	// Get URLs from NSWorkspace
	let workspaceURLs = NSWorkspace.shared.urlsForApplications(toOpen: .html)
	let workspaceHandlers = Set(workspaceURLs.compactMap { url -> String? in
		Bundle(url: url)?.bundleIdentifier
	})

	// Get handlers from Launch Services
	let lsHandlers = Set(LSCopyAllRoleHandlersForContentType(kUTTypeHTML, .viewer)?.takeRetainedValue() as? [String] ?? [])

	// Find differences
	let onlyInWorkspace = workspaceHandlers.subtracting(lsHandlers)
	let onlyInLaunchServices = lsHandlers.subtracting(workspaceHandlers)

	// Print results
	print("Only in NSWorkspace:")
	for bundleID in onlyInWorkspace.sorted() {
		if let appURL = NSWorkspace.shared.urlForApplication(withBundleIdentifier: bundleID) {
			print("- \(bundleID) (\(appURL.lastPathComponent))")
		} else {
			print("- \(bundleID)")
		}
	}

	print("\nOnly in Launch Services:")
	for bundleID in onlyInLaunchServices.sorted() {
		if let appURL = NSWorkspace.shared.urlForApplication(withBundleIdentifier: bundleID) {
			print("- \(bundleID) (\(appURL.lastPathComponent))")
		} else {
			print("- \(bundleID)")
		}
	}
}

compareHTMLHandlers()

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions