Build Custom Modules
for Itchy
Write a Swift bundle, expose one plugin, and choose whether it should appear as a Nook module or as a custom menu/content tab in the notch header. Users can import multiple bundles, and each one appears as its own plugin.
Get the SDK
The public SDK lives in the ItchySDK package on GitHub. Add it from Xcode as a Swift package dependency, then write your module with import itchy.
Swift Package repo: https://github.com/selcuksarikoz/itchy-sdk
The repo also includes example templates for Nook and menu plugins, including Clock, Counter, Date, Quick Actions, and Mini Shelf.
How it works
Itchy scans its Application Support modules folder for compiled macOS bundles, loads each bundle's principal class, and checks that it conforms to ItchyModulePlugin. If valid, the plugin appears in Settings and can be enabled, disabled, removed, and, for Nook placement, reordered with built-in modules.
Folders like Templates/DateModule/ are only example source code. The thing you import into Itchy is the compiled output, for example DateModule.bundle.
Itchy draws the module title from metadata.displayName. Custom module views should usually render only the content area and keep their backgrounds transparent so they match built-in modules.
You must explicitly choose one in source code: placement: .nookModule or placement: .menuApp. Itchy uses that to decide where the imported plugin appears inside the app.
Build a bundle
Create a macOS bundle target in Swift and expose a principal class that conforms to the plugin protocol.
Import it
Open Itchy Settings, go to Modules, and use Import Module to add the compiled .bundle.
Use it
Nook plugins render with built-ins, while menu plugins show up as additional icons in the top bar and open their own content area.
1. Plugin entry point
import AppKit
import SwiftUI
import itchy
@objc(MyClockModule)
final class MyClockModule: NSObject, ItchyModulePlugin {
var metadata: ItchyModuleMetadata {
ItchyModuleMetadata(
identifier: "com.example.clock",
displayName: "Clock",
summary: "A custom clock module",
preferredWidth: 220,
placement: .nookModule,
iconSystemName: "clock"
)
}
func makeViewController() -> NSViewController {
NSHostingController(rootView: ClockModuleView())
}
}2. SwiftUI view
import SwiftUI
struct ClockModuleView: View {
@State private var now = Date()
var body: some View {
TimelineView(.periodic(from: .now, by: 1)) { context in
VStack(alignment: .leading, spacing: 8) {
Text(context.date.formatted(date: .omitted, time: .standard))
.font(.system(size: 28, weight: .bold, design: .rounded))
.foregroundStyle(.white)
}
.frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .topLeading)
}
}
}3. Bundle 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>CFBundleIdentifier</key>
<string>com.example.itchy.clock</string>
<key>CFBundleName</key>
<string>ClockModule</string>
<key>CFBundlePackageType</key>
<string>BNDL</string>
<key>NSPrincipalClass</key>
<string>MyClockModule</string>
</dict>
</plist>A .bundle is a macOS package directory. In Finder it may look like a single file, but it is actually a folder with a compiled binary inside.
MyModule.bundle/
Contents/
Info.plist
MacOS/
MyModule- Build your plugin target so the output is a macOS
.bundle. - Start by adding the
ItchySDKSwift package from GitHub to your Xcode project. - Open Itchy and go to
Settings > Modules > Custom Modules. - Click
Import Moduleand select the bundle. - Enable the plugin from the matching Nook or Menu section.
- If it is a Nook plugin, drag it into position with the other modules.
If you do not want to use Xcode, you can build a template bundle from Terminal and then import the resulting .bundle.
swift build
mkdir -p BuiltBundles/DateModule.bundle/Contents/MacOS \
BuiltBundles/DateModule.bundle/Contents/Resources
cp Templates/DateModule/Info.plist \
BuiltBundles/DateModule.bundle/Contents/Info.plist
swiftc -parse-as-library -emit-library -Xlinker -bundle \
-module-name DateModule \
-I .build/arm64-apple-macosx/debug/Modules \
Templates/DateModule/DateModule.swift \
Templates/DateModule/DateModuleView.swift \
.build/arm64-apple-macosx/debug/itchy.build/ItchyModulePlugin.swift.o \
-o BuiltBundles/DateModule.bundle/Contents/MacOS/DateModule- Build your module and locate the resulting
.bundlein Finder. - Open Finder and press
Shift + Command + G. - Paste
~/Library/Application Support/Itchy/Modules. - Drag your
.bundleinto that folder. - Reopen Itchy or reopen Settings so the module list refreshes.
- One imported bundle currently maps to one custom Nook module.
- One imported bundle currently maps to one plugin item.
- Users can import multiple bundles; Itchy loads all valid ones.
- Your module identifier must be unique and must not collide with built-in module IDs.
- Set
placementexplicitly to.nookModuleor.menuApp. - Keep width reasonable; Itchy uses your preferred width when placing Nook modules.
- Return a regular
NSViewControllerorNSHostingController. - Validate bundles before shipping with
swift run itchy-module-validator /path/to/MyModule.bundle.