Template for creating native MioIsland plugins. MioIsland is a macOS notch app that puts your AI agent activity in the hardware notch.
This template is a Swift .bundle plugin — the format MioIsland actually loads at runtime. (The old JSON-based template was deprecated when v2.0 shipped the native plugin system.)
# Use this template, then:
git clone https://github.com/YOUR_USERNAME/YOUR_PLUGIN.git
cd YOUR_PLUGIN
# Build and install locally
./build.sh install
# Restart MioIsland — your plugin appears in the header bar.Then edit Sources/MyPlugin.swift and rebuild.
.
├── Sources/
│ ├── MioPlugin.swift # The protocol — DO NOT change selectors
│ └── MyPlugin.swift # ← edit this
├── Info.plist # Bundle metadata (id / version / principal class)
├── build.sh # Build + sign + package + optional install
└── README.md
A plugin is a macOS .bundle directory containing:
- A compiled dynamic library at
Contents/MacOS/<ModuleName> - An
Info.plistwhoseNSPrincipalClasspoints to a Swift class - That class must conform to the
MioPlugin@objcprotocol
When MioIsland starts it scans ~/.config/codeisland/plugins/*.bundle, calls Bundle.principalClass.init(), then talks to your instance via responds(to:) + perform(_:). So:
- The protocol must be
@objc - Selectors must match the host's exactly (the
MioPlugin.swiftin this template is a verbatim copy of the host's protocol — leave it alone) - Your principal class is named
<ModuleName>.<ClassName>inInfo.plist
The protocol is small:
@objc protocol MioPlugin: AnyObject {
var id: String { get } // stable identifier
var name: String { get } // human-readable
var icon: String { get } // SF Symbol name
var version: String { get } // semver
func activate() // called once when loaded
func deactivate() // called when unloaded
func makeView() -> NSView // your main view
@objc optional func viewForSlot(_ slot: String, context: [String: Any]) -> NSView?
}You'll need to edit four things consistently:
Sources/MyPlugin.swift— rename the class, changeid/name/icon/versionInfo.plist— changeCFBundleIdentifier/CFBundleName/CFBundleExecutable/NSPrincipalClassbuild.sh— updatePLUGIN_NAME(kebab-case) andMODULE_NAME(PascalCase) at the top- (optional) Rename
Sources/MyPlugin.swiftto match your class name
The id in your Swift class must match the <plugin-id> part of CFBundleIdentifier (com.mioisland.plugin.<id>) and the <plugin-id>.bundle filename produced by build.sh.
The template builds arm64-only (Apple Silicon). For universal binaries, change build.sh to:
swiftc \
-emit-library \
-module-name "${MODULE_NAME}" \
-target arm64-apple-macos15.0 \
-sdk "$(xcrun --show-sdk-path)" \
-o build/arm64.dylib \
${SOURCES}
swiftc \
-emit-library \
-module-name "${MODULE_NAME}" \
-target x86_64-apple-macos15.0 \
-sdk "$(xcrun --show-sdk-path)" \
-o build/x86_64.dylib \
${SOURCES}
lipo -create build/arm64.dylib build/x86_64.dylib \
-output "${BUILD_DIR}/${BUNDLE_NAME}/Contents/MacOS/${MODULE_NAME}"- Push your plugin source to a GitHub repo
- Sign in to the MioIsland Developer Portal with GitHub
- Install our GitHub App on your repo so the marketplace can mirror the source for review
- Submit your plugin: name, description, icon, screenshots, and upload
build/<plugin-id>.zip - We review the source code (mirrored to a private Gitea instance) and approve
After approval users can install your plugin in two ways:
- One-click install URL pasted into MioIsland's
System Settings → Plugins → Install from URL - Direct download of the .zip
You can ship updates by bumping version (must be strictly greater than the previous approved version, semver format) and submitting a new build.
Working plugins to study:
- mio-plugin-music — Now Playing (Spotify / Apple Music) controls in the notch, with header slot
- mio-plugin-stats — Editorial-style daily/weekly stats with i18n and a Claude-powered editor's note
MIT for the template itself. Your plugin can choose any license you want.