How PlugInKit enables app extensions
App extensions or appexes perform a wide range of tasks, from providing support for file systems like ExFAT to generating thumbnails for QuickLook and enabling Spotlight to index the contents of files. Although they’re relatively old, macOS made major changes in their management in Ventura, and they’ve become popular in many third-party apps. Despite that, there’s remarkably little information about how appexes are managed. As a result, when they play up it’s not clear what you should do. This article tries to disperse that cloud of unknowing.
There are four relatively unknown services responsible for managing apps, their extensions and related services:
- LaunchServices, which plays a prominent part in managing app launch, and maintains a large and comprehensive database of information about known apps and their extensions;
- RunningBoard, which manages resources such as memory and GPU access for some apps and their extensions;
- PlugInKit, which manages appexes for QuickLook, Spotlight, and many other services;
- Duet Activity Scheduler (DAS), which dispatches and manages background activities.
At the heart of PlugInKit is its management daemon pkd
, in /usr/libexec, which relies on working files and folders buried in a locked directory deep in /var/folders. It maintains its own registry of appexes, to which it adds annotations such as when they were last managed, and whether they have been ‘elected’. You can follow activities in the log using the subsystem com.apple.PlugInKit
, and exchanges with com.apple.launchservices
and PlugInKit’s client services such as com.apple.quicklook.ThumbnailsAgent
, responsible for the generation of QuickLook thumbnails for files.
Discovery
Of the four services, only LaunchServices appears to maintain a database that persists across restarts, although it’s rumoured to be stored over more than one database. RunningBoard maintains a list of running processes, DAS maintains a list of activities that it’s scheduled to dispatch, and PlugInKit keeps a registry of appexes, but each of those appears to be built from scratch during startup.
Shortly after user login, PlugInKit is initialised and starts populating its registry by a process of discovery, apparently deriving its information from the LaunchServices database, where each installed appex is detailed. Among the information stored there for appexes is their PlugInKit dictionary (PKDict) with the NSExtensionPointIdentifier that sets what type of appex it is, and their SDK data, repeating that type information as the NSExtensionPointName.
PlugInKit looks up batches of appexes to register, grouped according to their NSExtensionPointName. The first of these is com.apple.textinputmethod-services
, providing text input methods such as Vietnamese and Traditional Chinese. After those come com.apple.FinderSync
and com.apple.fileprovider-nonui
, and so on through a set sequence until ending with com.apple.widgetkit-extension
.
Log entries for this discovery phase are remarkable as, provided appexes haven’t changed in the LaunchServices database, entries by PlugInKit are almost identical during every startup. This stability is particularly helpful over appex UUIDs: so long as the appex remains the same, its UUID and order in discovery will be the same as well.
Coverage and versions
Particularly in recent versions of macOS, LaunchServices appears to cast its net widely, adding apps to its database from almost any accessible source. This results in some of the features it supports listing multiple versions of apps, some of which aren’t installed in traditional Applications folders.
PlugInKit’s coverage appears more focussed, and its rules over which appex to use ensure that old versions are excluded. Although old versions are registered during discovery, PlugInKit normally only offers the most recent version and, if there are multiple copies of that, the last registered by its timestamp in the registry. It’s also more conservative about which appexes it recognises: while LaunchServices will happily add apps that aren’t stored in an Applications folder and have never been opened on that Mac, PlugInKit appears more cautious in those it registers.
This is best illustrated in a VM with the host Applications folder shared. Initially, before that folder has been opened in the VM, apps and appexes inside it aren’t offered in the Finder’s Open With menu, and appexes such as a QuickLook thumbnail previewer can’t be used. When that shared folder has been opened in the Finder, LaunchServices makes its apps available through Open With. But the thumbnail previewer is only registered with PlugInKit when its shared parent app has been run in the VM.
Continuous discovery
In addition to startup discovery, PlugInKit has automatic continuous discovery during normal running. This can be seen best when an app containing an appex is installed and launched. LaunchServices adds the new app and its appex to its database, in what its log entries refer to as seeding. This is reported to PlugInKit, which then performs re-discovery for all appexes with the same NSExtensionPointName, so adding the new appex to its registry. During this, PlugInKit informs the service using that type of appex, triggering deployment of the capabilities of the new appex.
For example, launching a new app containing an appex with the NSExtensionPointName of com.apple.quicklook.thumbnail
results in LaunchServices adding that app and its appex to its database, then PlugInKit performs another discovery of all appexes with that NSExtensionPointName. When the new appex is added to its registry, PlugInKit informs com.apple.quicklook.ThumbnailsAgent
of the new QuickLook thumbnail previewer, so it can be made available for creating thumbnails almost immediately.
Discovery is readily traced in the log, but even simpler to follow by opening successive windows in AppexIndexer, and following the UUIDs given there.
Removal of an app with an enclosed appex is also quickly reflected in PlugInKit’s registry, and the appex’s service is informed immediately to ensure that no attempt is made to run the deleted extension.
Practical consequences
- Features of appexes are made available by the subsystem they act in.
- PlugInKit informs an appex’s subsystem of its availability during discovery.
- PlugInKit’s discovery relies on updates to the LaunchServices database.
- Damage to or dysfunction of the LaunchServices database can therefore block or impair PlugInKit registration, in turn preventing correct function of the appex.
- Resetting the LaunchServices database will inevitably delay PlugInKit’s discovery, and could lead to malfunction of appexes, such as failure to generate QuickLook thumbnails.
- Controlling appexes is currently only available in System Settings > General > Login Items & Extensions.
- Controlling appexes using the
pluginkit
command tool is only temporary, and any changes made there will be reverted in subsequent discovery. - Investigating appex problems by examining
com.apple.PlugInKit
subsystem entries in the log is clear and straightforward, and individual appexes can readily be traced using their UUID.