Xcode Bundle Plugins & Distribution
For complex plugins with multiple source files, resources, or third-party dependencies, you can create a full Xcode project that builds a plugin bundle.
For simpler plugins, consider using Swift Source Plugins instead - they require no Xcode project.
Bundle Extensions
Each plugin type uses a specific bundle extension:
| Plugin Type | Extension | Protocol |
|---|---|---|
| Touch Bar | .btttouchbarplugin | BTTPluginInterface |
| Stream Deck | .bttstreamdeckplugin | BTTStreamDeckPluginInterface |
| Floating Menu Widget | .bttwidget | BTTFloatingMenuWidgetInterface |
| Action | .bttactionplugin | BTTActionPluginInterface |
| Trigger | .btttriggerplugin | BTTTriggerPluginInterface |
| Launcher | .bttlauncherplugin | BTTLauncherPluginInterface |
Project Setup
1. Clone the Template Project
git clone git@github.com:folivoraAI/BetterTouchToolPlugins.git
Open xcode-bundle-examples/BetterTouchToolPluginDevelopment.xcodeproj in Xcode. It contains sample plugins and the required BTTPluginSupport.framework.
2. Create a New Target
Add a new macOS Bundle target to the project. Set the bundle extension to match your plugin type (e.g. .bttwidget).
3. Link BTTPluginSupport.framework
Add BTTPluginSupport.framework to your target's Frameworks and Libraries. This framework provides:
BTTPluginFormItem- configuration form item classBTTFormConstants- form field type constants- All plugin protocol definitions from
BTTPluginInterface.h
4. Configure Info.plist
Every plugin bundle must include these Info.plist keys:
| Key | Type | Description |
|---|---|---|
BTTPluginName | String | Display name shown in BTT |
BTTPluginIdentifier | String | Unique reverse-domain identifier (e.g. com.yourname.myplugin) |
BTTPluginType | String | One of: TouchBar, StreamDeck, FloatingMenuWidget, Action, Trigger, Launcher |
BTTPluginIcon | String | SF Symbol name (optional) |
NSPrincipalClass | String | Fully qualified class name |
For Swift classes, set NSPrincipalClass to $(PRODUCT_MODULE_NAME).ClassName:
<key>NSPrincipalClass</key>
<string>$(PRODUCT_MODULE_NAME).CPUUsagePlugin</string>
5. Implement the Protocol
Create your plugin class conforming to the appropriate protocol. See the individual plugin type pages for protocol details:
- Touch Bar Plugins
- Stream Deck Plugins
- Floating Menu Widget Plugins
- Action Plugins
- Trigger Plugins
- Launcher Plugins
6. Build and Test
Build the project in Xcode. Find the built bundle under the Products group (right-click > Show in Finder), then install it by double-clicking or dragging it onto the BTT preferences window.
Distribution
Plugins distributed to other users must be notarized by Apple. This requires an Apple Developer account.
1. Build/Archive
xcodebuild archive \
-scheme YourPluginScheme \
-configuration Release \
-archivePath ./build/yourplugin.xcarchive
cd build/yourplugin.xcarchive/Products/Library/Bundles/
2. Code Sign
codesign --deep \
-s "Developer ID Application: Your Name (TEAMID)" \
-f YourPlugin.bttwidget
Replace the identity string with your own Developer ID certificate and the extension with your plugin's bundle extension.
3. Zip for Notarization
ditto -c -k --keepParent --rsrc YourPlugin.bttwidget YourPlugin.notarize.zip
4. Submit for Notarization
xcrun notarytool submit YourPlugin.notarize.zip \
--apple-id "your@email.com" \
--team-id TEAMID \
--password "@keychain:notarization" \
--wait
5. Staple the Notarization Ticket
xcrun stapler staple YourPlugin.bttwidget
6. Zip for Distribution
ditto -c -k --keepParent --rsrc YourPlugin.bttwidget YourPlugin.zip
Share the resulting .zip file. Users install the plugin by unzipping it and double-clicking the bundle, or dragging it onto the BTT preferences window.
Swift source plugins (.swift files) do not require notarization and can be shared directly. Only compiled Xcode bundle plugins need to go through the notarization process.
Source Code & Examples
The full plugin source code, Xcode project templates, and sample plugins are available on GitHub:
https://github.com/folivoraAI/BetterTouchToolPlugins/tree/master/xcode-bundle-examples