From swift-development-assistant
Build macOS applications - AppKit, windows, menus, system integration, distribution
npx claudepluginhub pluginagentmarketplace/custom-plugin-swift --plugin swift-assistantThis skill uses the workspace's default tool permissions.
Native macOS application development with AppKit, system integration, and distribution.
Generates design tokens/docs from CSS/Tailwind/styled-components codebases, audits visual consistency across 10 dimensions, detects AI slop in UI.
Records polished WebM UI demo videos of web apps using Playwright with cursor overlay, natural pacing, and three-phase scripting. Activates for demo, walkthrough, screen recording, or tutorial requests.
Delivers idiomatic Kotlin patterns for null safety, immutability, sealed classes, coroutines, Flows, extensions, DSL builders, and Gradle DSL. Use when writing, reviewing, refactoring, or designing Kotlin code.
Native macOS application development with AppKit, system integration, and distribution.
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
| Component | Purpose |
|---|---|
| NSWindow | Window management |
| NSViewController | View controller |
| NSView | Base view class |
| NSMenu | Menu bar and context menus |
| NSStatusItem | Menu bar icon |
| NSAlert | Dialog boxes |
| Type | Use Case |
|---|---|
| NSWindow | Standard window |
| NSPanel | Utility/floating window |
| NSPopover | Attached popover |
| NSSavePanel/NSOpenPanel | File dialogs |
| Method | Requirements |
|---|---|
| Mac App Store | Sandbox, App Review |
| Developer ID | Notarization |
| Direct | None (Gatekeeper blocks) |
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()
}
}
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
}
}
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
}
#!/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"
| Issue | Cause | Solution |
|---|---|---|
| "App is damaged" | Not notarized | Notarize before distribution |
| Sandbox violation | Missing entitlement | Add required entitlement |
| Window not appearing | Wrong activation policy | Check NSApp.activationPolicy |
| Menu not responding | Missing target/action | Verify menu item connections |
| Sparkle updates fail | Code signing issue | Add exception entitlement |
# 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:
- 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
Skill("swift-macos")
swift-swiftui - SwiftUI on macOSswift-architecture - App architectureswift-testing - Testing macOS apps