Swiftpm app bundle
Skill chsistrying/swift-ship-skills/skills/swiftpm-app-bundle
Agent Skills for shipping Swift/macOS apps: .icns icons, .app/DMG packaging, CI portability traps, OSS readiness audit, release flow. Claude Code plugin marketplace.
npx -y skills add chsistrying/swift-ship-skills --skill swiftpm-app-bundleAssembled from the repository path, not quoted from the project. Check it against their README if it does not work.
2 things to look at
- 17 days oldThe repository was created 17 days ago. New is not bad, but a brand new repository carrying a familiar-sounding name is the shape a typosquat arrives in, and there has been no time for anyone else to find a problem with it.
- 0 stars0 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.
What its author says it does
Copied from the file, not written here
Package a Swift Package Manager (SwiftPM) executable target — an AppKit or SwiftUI app built without an Xcode project — into a distributable macOS .app bundle and an unsigned .dmg. Use when the user wants to package .app, build a DMG, turn a SwiftPM executable to app bundle, prepare a menu bar app distribution, ship a Swift command-line/GUI target as a double-clickable Mac app, or asks about Info.plist / .icns / LSUIElement / codesign / notarization for a package-based (no .xcodeproj) macOS app. Trigger with "/swiftpm-app-bundle".
The file declares its own license as MIT. That is the author’s claim about this one file, and it is not the same thing as the license GitHub reports for the repository, which is listed with the other numbers below.
SKILL.md
8.0 KB, ~1.9k tokens by cl100k_base, as published. Nobody here has run it
SwiftPM App Bundle
Overview
Turns a SwiftPM .executableTarget into a real macOS .app bundle plus an
unsigned .dmg, with no Xcode project involved. This is the common path for
small AppKit/SwiftUI utilities (menu bar apps, single-window tools) that are
built with swift build and never had an .xcodeproj to begin with.
Prerequisites
- macOS 12+ with Xcode Command Line Tools (
swift,codesign,xcrun). hdiutil(bundled with macOS) for DMG creation.- A
Package.swiftwith at least one.executableproduct.
When to use this skill
- The repo has a
Package.swiftwith an.executableproduct but no.appbundle, no.xcodeproj/.xcworkspace, and the user wants something they (or testers) can double-click, or a.dmgto hand out. - The user mentions: "package .app", "make a DMG", "SwiftPM executable to app bundle", "menu bar app distribution", "ship this as a Mac app".
What it does NOT do
- Does not sign or notarize anything (see
references/signing-notarization.mdfor that — it requires a paid Apple Developer account). - Does not create an Xcode project. If the user actually wants a proper Xcode-managed app target, that's a different task — ask before assuming.
Usage
From the repo root (the directory containing Package.swift):
scripts/package_app.sh
With no flags it will:
- Auto-detect the app name from the first
.executable(name: "...")product inPackage.swift. swift build -c releasethat product.- Assemble
dist/<App>-<version>-unsigned-<timestamp>/<App>.app. - Stage a DMG with the
.appplus an/Applicationssymlink and build<App>-<version>-unsigned.dmgin the same output directory.
Common flags (scripts/package_app.sh --help for the full list):
| Flag | Purpose |
|---|---|
--app-name NAME | Override auto-detection |
--bundle-id ID | Default is com.example.<lowercased-app-name> — always override this for a real release |
--version STRING | Default 0.1.0 |
--min-os STRING | LSMinimumSystemVersion, default 13.0 |
--menu-bar | Sets LSUIElement=true (no Dock icon / no app switcher entry) |
--icon PATH | Embed a .icns; auto-picked up from assets/<AppName>.icns if present |
--skip-dmg | Build just the .app, skip DMG staging |
Both flags and env vars work (APP_NAME=, BUNDLE_ID=, VERSION=, etc. —
see the script's --help output for the full mapping).
Examples
# Menu bar app with a real bundle id and an icon
scripts/package_app.sh --menu-bar --bundle-id com.example.tokenscope \
--version 0.2.0 --icon assets/TokenScope.icns
# Just the .app, no DMG, for quick local testing
scripts/package_app.sh --skip-dmg
Output
A timestamped dist/<App>-<version>-unsigned-<timestamp>/ directory containing
the assembled <App>.app bundle and (unless --skip-dmg) a compressed
<App>-<version>-unsigned.dmg staged with the standard drag-to-Applications
layout.
Detecting the app name from Package.swift
The script greps for the first .executable(name: "X" occurrence. When
briefing a user or writing this by hand, the same rule applies: open
Package.swift, find the products: [ .executable(name: "X", ...) ] entry.
If there are multiple executable products, ask the user which one they mean,
or pass --app-name / --product explicitly (--product matters when the
Swift product name differs from the desired display/bundle name).
When to set LSUIElement (menu-bar-only apps)
Set --menu-bar (→ LSUIElement=true) when the app:
- Lives in the menu bar via
NSStatusItem/MenuBarExtraand has no main window the user is meant to switch to via Cmd-Tab, or - Should not show a Dock icon or appear in the Cmd-Tab app switcher.
Leave it unset (LSUIElement=false, the default) for a normal windowed app
that should appear in the Dock and app switcher as usual. Check the source for
NSStatusBar.system.statusItem or MenuBarExtra in SwiftUI as a strong signal
the app is menu-bar-only.
DMG staging layout
The script stages the DMG contents in a scratch dmg/ folder before calling
hdiutil create -format UDZO:
dmg/
├── YourApp.app (copy of the built bundle)
└── Applications -> /Applications (symlink)
This is the standard "drag YourApp.app onto Applications" layout users expect
from a Mac installer DMG. Don't skip the symlink — without it, users have to
manually drag the app to /Applications via Finder navigation instead of a
single drag within the mounted window.
Failure modes
- Missing
NSHighResolutionCapable— omitting this key (or leaving itfalse) makes the app render blurry on Retina displays. The template always sets ittrue; don't remove it when hand-editing a plist. - Forgetting
chmod 755on the binary —cppreserves source permissions, which can be644from some build/copy pipelines, silently producing an app that Finder shows a "no entry" cursor for on launch. The script always doeschmod 755on the copied executable; verify this if the bundling steps are done manually instead of via the script. - Cached icon not updating on rebuild — macOS's icon cache can keep
showing an old icon for an app bundle after replacing its
.icnsorInfo.plist, especially when the app path is reused (same dist folder, overwritten in place). The script mitigates this by giving each build its own timestamped output directory and by runningxattr -cr+touchon the finished bundle. If a stale icon still appears after installing a new build, tell the user to either (a) move/rename the.apponce, or (b) runkillall Finderand/orqlmanage -r cacheand relaunch. CFBundleIdentifierleft as the placeholder default — the script defaults tocom.example.<name>, which is fine for local testing but should always be overridden with a real reverse-DNS identifier before distributing to anyone else (needed later for signing/notarization too).- Building the wrong product — if
Package.swiftdefines multiple executables,--app-namealone builds a product of that same name; use--productwhen the binary/product name differs from the desired app display name. - Running from the wrong directory — the script requires
Package.swiftat the repo root it's given (--repo-root, defaults to cwd). If it errors with "no Package.swift found", cd to the repo root or pass--repo-root. - Distributing the unsigned DMG and getting "damaged" complaints — this
is expected Gatekeeper behavior for unsigned software, not a packaging bug.
Point users to
references/signing-notarization.mdfor both the right-click-to-open workaround and the real signing/notarization path.
Resources
scripts/package_app.sh— the packaging script described above.assets/Info.plist.template— parameterized plist consumed by the script (placeholders:__APP_NAME__,__BUNDLE_ID__,__VERSION__,__MIN_MACOS_VERSION__,__LSUIELEMENT_BOOL__, and an@@ICON_BLOCK@@marker line that expands to theCFBundleIconFilekey/value pair or is dropped entirely when no icon is supplied).references/signing-notarization.md— what unsigned distribution means for end users, and the full codesign/notarytool/stapler upgrade path.
What ships with it: 3 files
12.2 KB alongside SKILL.md, 1 of them executable
assets/
- Info.plist.template993 B
references/
- signing-notarization.md3.2 KB
scripts/
- package_app.shruns8.0 KB