Search tldr

Building and Shipping Mac and iOS Apps Without Ever Opening Xcode

A fully headless Apple-app workflow can build, sign, notarize, staple, install, and deploy apps using Xcode’s command-line toolchain. After one-time credential setup, a generated project and release script let developers or coding agents ship without opening Xcode.

Share

Building and Shipping Mac and iOS Apps Without Ever Opening Xcode

Author: Scott Willsey | Published: 2026-07-11 | Generated: 2026-07-14 | Domain: scottwillsey.com
Tags: ‘#macOS’ ‘#iOS’ ‘#Xcode’ ‘#codesigning’ ‘#notarization’ ‘#automation’


TLDR

Xcode.app remains necessary because it contains Apple’s full SDK and release tools, but its GUI is not required for day-to-day development or shipping. With XcodeGen, keychain-backed signing and notarization credentials, a release.sh pipeline can regenerate projects, archive, Developer ID-sign, notarize, staple, verify, and install a Mac app entirely from the shell; iOS device deployment similarly uses xcodebuild and devicectl. The only unavoidable interactive work is initial Apple-account, certificate, and notarization-credential setup.

Key Takeaways

  • Use the full Xcode toolchain: xcode-select -p should resolve to /Applications/Xcode.app/Contents/Developer, not standalone /Library/Developer/CommandLineTools, which lacks the iOS SDK, notarytool, and devicectl.
  • Generate projects instead of committing them: XcodeGen recreates the .xcodeproj from a version-controlled project.yml, avoiding Xcode project-file churn in Git.
  • Separate fast builds from shippable builds: CODE_SIGNING_ALLOWED=NO supports quick local/CI compilation and simulator use, but Gatekeeper rejects the output and team-bound entitlements such as iCloud KVS and App Groups do not bind.
  • Automate the Mac release chain: A script can run xcodegen generate, xcodebuild archive, xcodebuild -exportArchive with developer-id, notarytool submit --wait, stapler staple, spctl verification, and installation to /Applications; set -euo pipefail ensures it stops at the first failure.
  • Keep credentials out of source control: Developer ID certificates and private keys live in the login keychain, while an app-specific password is stored once in a notarytool keychain profile. The private key cannot be re-downloaded, so it should be backed up.
  • Deploy iOS devices headlessly: Build with an Apple Development identity and provisioning via xcodebuild -allowProvisioningUpdates, then install the resulting app with xcrun devicectl device install app.

Images & Media

Keep reading