# Workflow: Ship/Release a macOS App **Read these reference files NOW:** 1. references/security-code-signing.md 2. references/cli-workflow.md ## 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 ```bash # 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):** ```bash # 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 method developer-id signingStyle automatic ``` **For App Store:** ```xml method app-store ``` ## Step 4: Notarization (Direct Distribution) Required for apps distributed outside the App Store: ```bash # 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) ```bash # 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 ```bash # 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:** ```bash # 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 | 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 |