Developer Docs

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.

1

Build a bundle

Create a macOS bundle target in Swift and expose a principal class that conforms to the plugin protocol.

2

Import it

Open Itchy Settings, go to Modules, and use Import Module to add the compiled .bundle.

3

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.

Example

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>
Bundle Format

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
Import Flow
  1. Build your plugin target so the output is a macOS .bundle.
  2. Start by adding the ItchySDK Swift package from GitHub to your Xcode project.
  3. Open Itchy and go to Settings > Modules > Custom Modules.
  4. Click Import Module and select the bundle.
  5. Enable the plugin from the matching Nook or Menu section.
  6. If it is a Nook plugin, drag it into position with the other modules.
Terminal Build

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
Manual Install
  1. Build your module and locate the resulting .bundle in Finder.
  2. Open Finder and press Shift + Command + G.
  3. Paste ~/Library/Application Support/Itchy/Modules.
  4. Drag your .bundle into that folder.
  5. Reopen Itchy or reopen Settings so the module list refreshes.
Rules
  • 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 placement explicitly to .nookModule or .menuApp.
  • Keep width reasonable; Itchy uses your preferred width when placing Nook modules.
  • Return a regular NSViewController or NSHostingController.
  • Validate bundles before shipping with swift run itchy-module-validator /path/to/MyModule.bundle.