Files
gh-glittercowboy-taches-cc-…/skills/expertise/macos-apps/workflows/ship-app.md
2025-11-29 18:28:37 +08:00

3.9 KiB

Workflow: Ship/Release a macOS App

<required_reading> Read these reference files NOW:

  1. references/security-code-signing.md
  2. references/cli-workflow.md </required_reading>
## Step 1: Prepare for Release

Ensure the app is ready:

  • All features complete and tested
  • No debug code or test data
  • Version and build numbers updated in Info.plist
  • App icon and assets finalized
# Verify build succeeds
xcodebuild -project AppName.xcodeproj -scheme AppName -configuration Release build

Step 2: Choose Distribution Method

Method Use When Requires
Direct distribution Sharing with specific users, beta testing Developer ID signing + notarization
App Store Public distribution, paid apps App Store Connect account, review
TestFlight Beta testing at scale App Store Connect

Step 3: Code Signing

For Direct Distribution (Developer ID):

# Archive
xcodebuild -project AppName.xcodeproj \
  -scheme AppName \
  -configuration Release \
  -archivePath ./build/AppName.xcarchive \
  archive

# Export with Developer ID
xcodebuild -exportArchive \
  -archivePath ./build/AppName.xcarchive \
  -exportPath ./build/export \
  -exportOptionsPlist ExportOptions.plist

ExportOptions.plist for Developer ID:

<?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>method</key>
    <string>developer-id</string>
    <key>signingStyle</key>
    <string>automatic</string>
</dict>
</plist>

For App Store:

<key>method</key>
<string>app-store</string>

Step 4: Notarization (Direct Distribution)

Required for apps distributed outside the App Store:

# Submit for notarization
xcrun notarytool submit ./build/export/AppName.app.zip \
  --apple-id "your@email.com" \
  --team-id "TEAMID" \
  --password "@keychain:AC_PASSWORD" \
  --wait

# Staple the ticket
xcrun stapler staple ./build/export/AppName.app

Step 5: Create DMG (Direct Distribution)

# Create DMG
hdiutil create -volname "AppName" \
  -srcfolder ./build/export/AppName.app \
  -ov -format UDZO \
  ./build/AppName.dmg

# Notarize the DMG too
xcrun notarytool submit ./build/AppName.dmg \
  --apple-id "your@email.com" \
  --team-id "TEAMID" \
  --password "@keychain:AC_PASSWORD" \
  --wait

xcrun stapler staple ./build/AppName.dmg

Step 6: App Store Submission

# Validate
xcrun altool --validate-app \
  -f ./build/export/AppName.pkg \
  -t macos \
  --apiKey KEY_ID \
  --apiIssuer ISSUER_ID

# Upload
xcrun altool --upload-app \
  -f ./build/export/AppName.pkg \
  -t macos \
  --apiKey KEY_ID \
  --apiIssuer ISSUER_ID

Then complete submission in App Store Connect.

Step 7: Verify Release

For direct distribution:

# Verify signature
codesign -dv --verbose=4 ./build/export/AppName.app

# Verify notarization
spctl -a -vv ./build/export/AppName.app

For App Store:

  • Check App Store Connect for review status
  • Test TestFlight build if applicable
Before shipping: - [ ] Version number incremented - [ ] Release notes written - [ ] Debug logging disabled or minimized - [ ] All entitlements correct and minimal - [ ] Privacy descriptions in Info.plist - [ ] App icon complete (all sizes) - [ ] Screenshots prepared (if App Store) - [ ] Tested on clean install

<common_issues>

Issue Cause Fix
Notarization fails Unsigned frameworks, hardened runtime issues Check all embedded binaries are signed
"App is damaged" Not notarized or stapled Run notarytool and stapler
Gatekeeper blocks Missing Developer ID Sign with Developer ID certificate
App Store rejection Missing entitlement descriptions, privacy issues Add usage descriptions to Info.plist
</common_issues>