mcpField
S
swift-macos

swift-macos

github.com/pluginagentmarketplaceDevelopment848 installsUpdated 43w ago
Repository

About swift-macos

swift-macos is a skill for building native macOS applications with AppKit, system integration, and distribution guidance. It is aimed at developers working with macOS interfaces, sandboxing, signing, and release workflows.

Key features

  1. 01AppKit components and window types
  2. 02Menu bar and document-based app examples
  3. 03Sandboxed file access with bookmarks
  4. 04App Store and Developer ID distribution
  5. 05Signing, notarization, and entitlement checks

Use cases

  1. 01Build a native macOS window or menu app
  2. 02Add sandboxed file access
  3. 03Prepare an app for distribution
  4. 04Diagnose signing or notarization issues

Install

Copy the command and run it in a terminal.

$npx skills add https://github.com/pluginagentmarketplace/custom-plugin-swift --skill swift-macos

After installing, type/swift-macosin Claude Code to confirm it loaded.

Bundled files

References and scripts installed along with SKILL.md.

assets/
.gitkeep
AppDelegate.swift.template
config.yaml
references/
.gitkeep
GUIDE.md
scripts/
.gitkeep
notarize.sh

Details

From the project README

Swift macOS Skill

Native macOS application development with AppKit, system integration, and distribution.

Prerequisites

  • Xcode 15+ on macOS Sonoma+
  • Apple Developer account (for distribution)
  • Understanding of AppKit fundamentals

Parameters

parameters:
  distribution_method:
    type: string
    enum: [app_store, developer_id, none]
    default: developer_id
  sandbox_enabled:
    type: boolean
    default: true
  min_macos_version:
    type: string
    default: "13.0"
  app_type:
    type: string
    enum: [document_based, single_window, menu_bar, agent]
    default: single_window

Topics Covered

AppKit Components
ComponentPurpose
NSWindowWindow management
NSViewControllerView controller
NSViewBase view class
NSMenuMenu bar and context menus
NSStatusItemMenu bar icon
NSAlertDialog boxes
Window Types
TypeUse Case
NSWindowStandard window
NSPanelUtility/floating window
NSPopoverAttached popover
NSSavePanel/NSOpenPanelFile dialogs
Distribution Methods
MethodRequirements
Mac App StoreSandbox, App Review
Developer IDNotarization
DirectNone (Gatekeeper blocks)

Code Examples

Menu Bar Application
import AppKit
import SwiftUI

@main
struct MenuBarApp: App {
    @NSApplicationDelegateAdaptor(AppDelegate.self) var appDelegate

    var body: some Scene {
        Settings {
            SettingsView()
        }
    }
}

final class AppDelegate: NSObject, NSApplicationDelegate {
    private var statusItem: NSStatusItem!
    private var popover: NSPopover!

    func applicationDidFinishLaunching(_ notification: Notification) {
        setupStatusItem()
        setupPopover()

        // Hide dock icon for menu bar only app
        NSApp.setActivationPolicy(.accessory)
    }

    private func setupStatusItem() {
        statusItem = NSStatusBar.system.statusItem(withLength: NSStatusItem.squareLength)

        if let button = statusItem.button {
            button.image = NSImage(systemSymbolName: "star.fill", accessibilityDescription: "App")
            button.action = #selector(togglePopover)
        }
    }

    private func setupPopover() {
        popover = NSPopover()
        popover.contentSize = NSSize(width: 300, height: 400)
        popover.behavior = .transient
        popover.contentViewController = NSHostingController(rootView: PopoverView())
    }

    @objc private func togglePopover() {
        guard let button = statusItem.button else { return }

        if popover.isShown {
            popover.performClose(nil)
        } else {
            popover.show(relativeTo: button.bounds, of: button, preferredEdge: .minY)
            popover.contentViewController?.view.window?.makeKey()
        }
    }
}

struct PopoverView: View {
    @Environment(\.openSettings) private var openSettings

    var body: some View {
        VStack(spacing: 16) {
            Text("Menu Bar App")
                .font(.headline)

            Button("Settings") {
                openSettings()
            }

            Button("Quit") {
                NSApp.terminate(nil)
            }
        }
        .padding()
    }
}
NSDocument-Based App
import AppKit

final class Document: NSDocument {
    var content: String = ""

    override class var autosavesInPlace: Bool { true }

    override func makeWindowControllers() {
        let contentVC = ContentViewController(document: self)
        let window = NSWindow(contentViewController: contentVC)
        window.setContentSize(NSSize(width: 800, height: 600))

        let windowController = NSWindowController(window: window)
        addWindowController(windowController)
    }

    override func data(ofType typeName: String) throws -> Data {
        guard let data = content.data(using: .utf8) else {
            throw NSError(domain: NSOSStatusErrorDomain, code: writErr)
        }
        return data
    }

    override func read(from data: Data, ofType typeName: String) throws {
        guard let content = String(data: data, encoding: .utf8) else {
            throw NSError(domain: NSOSStatusErrorDomain, code: readErr)
        }
        self.content = content
    }
}

final class ContentViewController: NSViewController {
    private let document: Document

    init(document: Document) {
        self.document = document
        super.init(nibName: nil, bundle: nil)
    }

    required init?(coder: NSCoder) {
        fatalError("init(coder:) not implemented")
    }

    override func loadView() {
        let scrollView = NSScrollView()
        let textView = NSTextView()

        textView.isEditable = true
        textView.isRichText = false
        textView.font = .monospacedSystemFont(ofSize: 13, weight: .regular)
        textView.string = document.content

        scrollView.documentView = textView
        scrollView.hasVerticalScroller = true

        view = scrollView
    }
}
Sandboxing with Security-Scoped Bookmarks
final class SandboxedFileAccess {
    private static let bookmarksKey = "securityScopedBookmarks"

    static func requestAccess(to url: URL) throws -> URL {
        let bookmark = try url.bookmarkData(
            options: .withSecurityScope,
            includingResourceValuesForKeys: nil,
            relativeTo: nil
        )

        var bookmarks = UserDefaults.standard.dictionary(forKey: bookmarksKey) as? [String: Data] ?? [:]
        bookmarks[url.path] = bookmark
        UserDefaults.standard.set(bookmarks, forKey: bookmarksKey)

        return url
    }

    static func accessBookmarkedURL(_ path: String) throws -> URL {
        guard let bookmarks = UserDefaults.standard.dictionary(forKey: bookmarksKey) as? [String: Data],
              let bookmarkData = bookmarks[path] else {
            throw SandboxError.noBookmark
        }

        var isStale = false
        let url = try URL(
            resolvingBookmarkData: bookmarkData,
            options: .withSecurityScope,
            relativeTo: nil,
            bookmarkDataIsStale: &isStale
        )

        if isStale {
            _ = try requestAccess(to: url)
        }

        return url
    }

    static func withAccess<T>(to url: URL, perform: (URL) throws -> T) throws -> T {
        guard url.startAccessingSecurityScopedResource() else {
            throw SandboxError.accessDenied
        }
        defer { url.stopAccessingSecurityScopedResource() }
        return try perform(url)
    }
}

enum SandboxError: Error {
    case noBookmark
    case accessDenied
}
Notarization Script
#!/bin/bash
set -e

APP_PATH="$1"
DEVELOPER_ID="Developer ID Application: Your Name (TEAMID)"
KEYCHAIN_PROFILE="notary-profile"

echo "Signing..."
codesign --force --options runtime --sign "$DEVELOPER_ID" \
    --deep --strict "$APP_PATH"

echo "Creating ZIP..."
ditto -c -k --keepParent "$APP_PATH" "app.zip"

echo "Submitting for notarization..."
xcrun notarytool submit "app.zip" \
    --keychain-profile "$KEYCHAIN_PROFILE" \
    --wait

echo "Stapling..."
xcrun stapler staple "$APP_PATH"

echo "Verifying..."
spctl --assess --type execute --verbose=2 "$APP_PATH"

echo "Done!"
rm "app.zip"

Troubleshooting

Common Issues
IssueCauseSolution
"App is damaged"Not notarizedNotarize before distribution
Sandbox violationMissing entitlementAdd required entitlement
Window not appearingWrong activation policyCheck NSApp.activationPolicy
Menu not respondingMissing target/actionVerify menu item connections
Sparkle updates failCode signing issueAdd exception entitlement
Debug Tips
# Check code signing
codesign -dv --verbose=4 App.app

# Check entitlements
codesign -d --entitlements :- App.app

# Check notarization
spctl --assess --type execute App.app

# View sandbox violations
log show --predicate 'subsystem == "com.apple.sandbox"' --last 1h

Validation Rules

validation:
  - rule: hardened_runtime
    severity: error
    check: Enable hardened runtime for notarization
  - rule: sandbox_entitlements
    severity: warning
    check: Only request necessary sandbox entitlements
  - rule: code_signing
    severity: error
    check: Sign with Developer ID for distribution

Usage

Skill("swift-macos")

Related Skills

  • swift-swiftui - SwiftUI on macOS
  • swift-architecture - App architecture
  • swift-testing - Testing macOS apps

Comments

Markdown supported