What makes an extension valid inside an app bundle: the Info.plist, the bundle id prefix, and where it is installed?

19.10.1 · L3 · Interview

Predict first

What do plutil -lint and -extract report for this plist?

// === Info.plist ===
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0"><dict>
<key>NSExtension</key><dict>
  <key>NSExtensionPointIdentifier</key><string>com.apple.widgetkit-extension</string>
</dict></dict></plist>
  • Info.plist: OK, then com.apple.widgetkit-extension
  • Info.plist: OK, then a dictionary
  • A syntax error

Mental model. The prefix rule is the same as a flat number inside a building: 12B must belong to number 12, or the post office rejects the delivery.

An extension’s Info.plist is small: the NSExtension dictionary with NSExtensionPointIdentifier (for a widget, com.apple.widgetkit-extension; a widget needs no principal class because the @main bundle is the entry point). plutil -lint Info.plist checks the syntax and plutil -extract NSExtension.NSExtensionPointIdentifier raw Info.plist prints the value, so both are quick CI checks. The built app embeds the extension at Bank.app/PlugIns/Widget.appex (in a built sample, with CFBundlePackageType = XPC!), and xcrun simctl spawn booted pluginkit -m -v then listed com.example.Bank.Widget with that path, which is the quickest way to confirm the system registered it.

The extension’s bundle id must be prefixed by the app’s. In a hand-made project, a widget with id com.other.Widget inside com.example.Bank built without an error but simctl install failed with “Mismatched bundle IDs … Failed to set app extension placeholders”. So a build that succeeds is not proof; install it. The version and build numbers should also match the app’s, as App Store validation requires. These install and pluginkit runs are recorded in NOTES.md (they need xcodebuild), only the plist check is in verify.py. Not run: App Store validation, which needs a signed archive.

The plist is well formed and the extract prints the raw string.

Common traps

  • Assuming a successful build means a valid bundle. The mismatch only appears on install.
  • Hand-editing the plist and skipping plutil -lint. A syntax error is only found at install.

They might follow up with

  • How do I list what the system sees? pluginkit -m -v, optionally with -p and an extension point id.

Related questions

  • 19.1.1 What is an app extension, how is it launched, and what limits do extensions run under?
  • 19.3.1 How do Share and Action extensions work, and how do they receive and return data?
  • 19.7.2 How do you treat extensions, widgets and intents as a public API of the product: shared modules, data…

What does an app group look like on disk, and what happens with a… →