DEV Community

iPhoneTechie
iPhoneTechie

Posted on

Upload IPA to App Store Without Xcode on Windows/Linux/Mac: upload Command Explained

Got a built and signed IPA? One command hands it to Apple—no Mac and no Xcode required:

appuploader-cli upload -f Payload.ipa -u dev@example.com -p abcd-efgh-ijkl-mnop
Enter fullscreen mode Exit fullscreen mode

appuploader-cli is the command-line tool bundled with AppUploader. The upload command submits a signed .ipa (iOS) or .pkg (macOS) to App Store Connect—the same thing as clicking "Submit to App Store" in the GUI, except it can be written into scripts.

Below, let's go through how to use this command: how to choose between the two login methods, how to upload non-iOS packages, how to tell whether it actually succeeded, and in what order to troubleshoot when uploads fail.

Find the Command

After installing AppUploader, the command-line tool is already installed with it:

OS Location
Windows appuploader-cli.exe in the same directory as the main program AppUploader.exe
macOS AppUploader.app/Contents/Resources/appuploader-cli
Linux appuploader-cli in the same directory as the main program AppUploader

Open a terminal in this directory and you can use it. To run it from any directory, add it to PATH: on Windows, go to Settings → System → About → Advanced system settings → Environment Variables → Path; after adding it, reopen the terminal for it to take effect. On macOS/Linux, add a line export PATH="$PATH:/your/install/directory" to ~/.zshrc or ~/.bashrc.

Also prepare a package that has already been signed—upload does not compile or sign; its input is the built .ipa or .pkg.

Login Method 1: App-Specific Password

The easiest credential for command-line uploads is an App-Specific Password. Apple designed it specifically for third-party tools, and it does not trigger two-factor authentication—you don't have to confirm on your phone every time you upload, which is essential for scripts and unattended build machines.

Go to https://account.apple.com/account/manage and sign in, find "App-Specific Passwords", create one, and you'll get a password like abcd-efgh-ijkl-mnop. It is shown only once, so save it immediately.

Then:

appuploader-cli upload -f Payload.ipa -u dev@example.com -p abcd-efgh-ijkl-mnop
Enter fullscreen mode Exit fullscreen mode

-u is the Apple ID email, -p is the newly generated app-specific password; both must be provided together. Specify the package path with -f, or omit -f and put it directly at the end of the command:

appuploader-cli upload -u dev@example.com -p abcd-efgh-ijkl-mnop Payload.ipa
Enter fullscreen mode Exit fullscreen mode

The most common mistake is putting your Apple ID login password in -p. An app-specific password is always 16 letters with three hyphens in the middle, completely different from a login password. Check it before entering it.

Login Method 2: App Store Connect API Key

If this command will run on a team build machine or pipeline, using someone's Apple ID is not ideal—when that person leaves or changes their password, the whole automation breaks. In this case, use an App Store Connect API key instead: it belongs to the team rather than an individual, can be revoked separately, and also does not involve two-factor authentication.

Go to https://appstoreconnect.apple.com/access/api to generate one (requires Account Holder or Admin permission). You need three things: the Key ID, the Issuer ID at the top of the page, and the .p8 private key file. The .p8 can only be downloaded once; Apple will not give it a second time. Store it immediately in a password manager or your CI's secret store.

appuploader-cli upload -f Payload.ipa \
  --api-key UK29KBAX9X \
  --api-issuer 69a6de78-4459-47e3-e053-5b8c7c11a4d1 \
  --private-key Auth.Key_UK29KBAX9X.p8
Enter fullscreen mode Exit fullscreen mode

All three parameters are required. The two login methods are mutually exclusive; mixing -u -p with these three parameters will be rejected outright—when switching from password to key, remember to remove the original -u -p completely.

Before using the key method, confirm two things:

  1. It can only upload .ipa. This method relies on the Bundle ID in the package to find the corresponding app on App Store Connect; it cannot be obtained from a .pkg. For macOS .pkg, use the app-specific password method above.
  2. This Bundle ID must already have an app created in App Store Connect. If it hasn't been created, you'll get an error saying the corresponding app cannot be found. Create it first, then upload.

Which One to Choose

In one sentence: for manual releases by yourself, use an App-Specific Password; for multi-person collaboration or pipelines, use an API key.

The app-specific password wins on simplicity—two parameters and it runs; but it is tied to an individual Apple ID, so handing it to a pipeline means handing over personal credentials. The API key takes a few more configuration steps, but in exchange it is decoupled from a person, can be revoked separately, and if something goes wrong you only need to rotate that one key.

Uploading Non-iOS Packages

By default it is treated as iOS; for other platforms, add --type:

# macOS
appuploader-cli upload -u dev@example.com -p abcd-efgh-ijkl-mnop --type osx App.pkg

# tvOS
appuploader-cli upload -f App.ipa -u dev@example.com -p abcd-efgh-ijkl-mnop --type appletvos
Enter fullscreen mode Exit fullscreen mode

There are four values in total: ios (default), osx, appletvos, xros (visionOS).

How to Tell Whether the Upload Actually Succeeded

After pressing Enter, progress will print line by line in real time; the larger the package, the longer it takes. Look at the last line:

> upload finished successfully
Enter fullscreen mode Exit fullscreen mode

On failure:

> upload finished with error: <specific reason from Apple>
Enter fullscreen mode Exit fullscreen mode

If you're writing a script, do not grep these logs—when the command fails, the exit code is non-zero; checking the exit code is enough, and it won't break when the log wording changes:

set -e
appuploader-cli upload -f build/App.ipa -u "$APPLE_ID" -p "$APP_PASSWORD"
Enter fullscreen mode Exit fullscreen mode

In Windows PowerShell:

& appuploader-cli.exe upload -f .\build\App.ipa -u $env:APPLE_ID -p $env:APP_PASSWORD
if ($LASTEXITCODE -ne 0) { throw "Upload failed" }
Enter fullscreen mode Exit fullscreen mode

One more thing that's easy to misunderstand: a successful command return only means Apple accepted the package and it passed initial validation. Apple still processes it for a few minutes to a few tens of minutes; only after processing does it appear in the "Builds" list in App Store Connect, and only then can you select it for review. Not seeing it immediately after upload is normal.

If Upload Fails, Troubleshoot in This Order

Category 1: The command exits before it even runs. These problems are local; you can verify immediately after fixing them.

  • file not found: the path is wrong. In CI, the most common cause is that the current working directory is not what you think it is; use absolute paths in scripts whenever possible.
  • Error saying the two login methods cannot be mixed: keep only one set of -u/-p or --api-key/--api-issuer/--private-key.
  • not a valid PEM file or expected PKCS8: --private-key does not point to the .p8 Apple gave you, or the file content is corrupted. In CI, the most common cause is losing line breaks when storing the .p8 in a Secret; copy the original file completely again and re-save it.
  • the ipa has no CFBundleIdentifier: you used the key method to upload a non-IPA package; switch back to the app-specific password method.

Category 2: The package uploaded, but Apple rejected it. These reasons are all written after upload finished with error:. Common ones:

  • Duplicate version/build number: the same CFBundleShortVersionString + CFBundleVersion combination has already been uploaded. Apple does not allow overwriting; change the build number and repackage.
  • Signing mismatch: the certificate or provisioning profile used by the package is not for App Store distribution. Check whether you selected an App Store type provisioning profile when packaging.
  • Missing Info.plist fields: common ones include missing icons, missing required usage descriptions (NSxxxUsageDescription for camera, photos, location, etc.), or missing certain device capability declarations. Add them according to the error and repackage.
  • Corresponding app not found: this Bundle ID has not had an app created in App Store Connect yet.

After this command is configured, you basically don't need to touch it again; just run it once when releasing or let CI run it. The same tool can also sign certificates, create Bundle IDs, and generate provisioning profiles on Windows; that's how I bypassed a Mac for the entire iOS release pipeline.

Top comments (0)