skip to content

For a Flutter iOS release, how do a distribution certificate and a provisioning profile differ, and when would you switch Xcode to manual signing?

level: middleimportance: must knowfreq 50%

answer

  1. identity versus permission slip
  2. private key lives in the keychain
  3. profile binds App ID, team, certificate
  4. Automatically manage signing is on by default
  5. CODE_SIGN_STYLE Manual plus a profile specifier

basics

~20 s

A distribution certificate is the signing identity, usable only with its private key in the Mac's keychain; a provisioning profile is Apple's file binding an App ID, team, certificate and entitlements. Manual signing fits machines without an Apple account session.

solid answer

~50 s

A **distribution certificate** proves who signed the app; it only signs if its **private key** is in the build Mac's keychain. A **provisioning profile** (`.mobileprovision`) is issued by Apple and says what may be signed: it ties the App ID (bundle identifier), the team, the allowed certificates and the entitlements, plus device UDIDs for development and ad-hoc builds. In a Flutter project these settings live on the Runner target in Xcode, where **Automatically manage signing** is on by default and Xcode fetches or renews both through the signed-in account. You switch the Release configuration to `CODE_SIGN_STYLE = Manual` with `DEVELOPMENT_TEAM` and `PROVISIONING_PROFILE_SPECIFIER` when the machine has no Apple account session, such as a CI Mac, or when a team must use one shared certificate. Since Flutter 3.41, `flutter build ipa` then generates a manual `ExportOptions.plist` for the main bundle.

go deeper

for a junior

Recall the split: certificate is who signed and needs its private key; profile is what may be signed and with which capabilities.

for a middle

Explain what a profile binds, why App Store profiles have no devices, and the three build settings that make the Runner target manually signed.

for a senior

Diagnose export failures: missing private key, profile not matching the bundle ID, and extensions left out of the plist Flutter generates for manual signing.

for a principal

Weigh automatic signing on each developer Mac against one shared, manually managed distribution identity, and who owns renewals and revocations.

## Why iOS needs two artefacts iOS runs only code signed in a way Apple can trace, so an App Store build needs two different things: - **Who signed it** — the **certificate**. - **What that signer may sign, and with which capabilities** — the **provisioning profile**. A Flutter app is no exception: the Dart code is compiled into the Runner app, and Xcode signs that bundle like any other iOS app. All signing settings live on the **Runner** target in `ios/Runner.xcworkspace`. ## The certificate A certificate is issued by Apple for a public key whose **private key** stays on the machine that created the signing request, in its **keychain**. Two kinds matter: - **Development** — signs builds you run on your own registered devices. - **Distribution** — signs App Store, ad-hoc and TestFlight builds. A `.cer` file alone cannot sign anything. The classic failure on a new Mac is a certificate that shows up in the developer account but has no matching private key locally; exporting it with its key (a `.p12`) is how it moves between machines. ## The provisioning profile A profile is an Apple-signed file that binds together: 1. the **App ID**, which must match the Runner target's `PRODUCT_BUNDLE_IDENTIFIER`; 2. the **team**; 3. the **certificates** allowed to sign; 4. the **entitlements** the app may use, such as push notifications or associated domains; 5. for development and ad-hoc profiles only, the **device UDIDs** allowed to install the build. An App Store profile carries no device list. Each app extension (a share extension or a widget) has its own bundle ID and so needs its own profile. | | Certificate | Provisioning profile | |---|---|---| | Answers | who signed | what may be signed, where, with which capabilities | | Secret part | the private key in the keychain | none; it is not secret | | Scope | one team, many apps | one App ID (or a wildcard) | | Typical file | `.cer`, `.p12` with the key | `.mobileprovision` | ## Automatic versus manual signing **Automatic signing** is Xcode's *Automatically manage signing* checkbox, which the Flutter deployment guide notes is on by default and sufficient for most apps. Xcode uses the signed-in account to create and renew certificates and profiles. `flutter build ipa` also passes `-allowProvisioningUpdates` and `-allowProvisioningDeviceRegistration` to `xcodebuild -exportArchive`, so Xcode may fetch what the export needs. **Manual signing** means the Release configuration names exactly what to use: - `CODE_SIGN_STYLE = Manual` - `DEVELOPMENT_TEAM = <team id>` - `PROVISIONING_PROFILE_SPECIFIER = <profile name or UUID>` Reasons to choose it: - a build machine, typically CI, with no interactive Apple account session; - a team that must sign with one shared distribution certificate instead of letting each Xcode create its own; - extensions or entitlements that need specific, pre-made profiles. ## What flutter build ipa does with manual signing Since **Flutter 3.41**, when the Release or Profile build uses `CODE_SIGN_STYLE=Manual` and the tool finds the named profile in `~/Library/Developer/Xcode/UserData/Provisioning Profiles`, it generates an `ExportOptions.plist` with `method`, `teamID`, `signingStyle` set to `manual` and a `provisioningProfiles` entry mapping the bundle ID to the profile's UUID. Its own source lists the limit: **only the main bundle ID** is mapped. An app with extensions, or a profile the tool cannot find, falls back to a plist with just the method, and the fix is to pass `--export-options-plist` with every bundle ID mapped. ## Reading a signing failure Most signing errors name one of the pieces above, so the diagnosis follows the same order: 1. **No signing identity** — the keychain has no certificate with its private key for this team. Import the `.p12` or create a certificate from this Mac. 2. **No matching profile** — the profile's App ID does not match `PRODUCT_BUNDLE_IDENTIFIER`, it is the wrong type (development instead of App Store), or it is not installed. The Flutter tool prints its own hint when Xcode reports that a target requires a provisioning profile. 3. **Profile does not include the certificate** — the profile was generated before the current certificate existed; regenerate it. 4. **Missing entitlement** — a capability such as push notifications is enabled in Xcode but not in the profile. On a CI Mac the same checks apply, with the extra step of getting the certificate and profile onto a machine that is wiped between jobs; how those secrets are stored and synced is a pipeline concern rather than a signing concept. ## Common misconceptions - The profile does not contain the private key; the keychain does. - Automatic signing can produce App Store builds; manual signing is a choice for control, not a requirement for release. - A development certificate cannot export an App Store IPA.

  • Why can a Flutter app with a share extension still fail export after switching to manual signing?
    The ExportOptions.plist that `flutter build ipa` generates for manual signing maps only the main bundle ID to its profile. The extension has its own bundle ID and needs its own profile, so pass `--export-options-plist` with a `provisioningProfiles` entry for every bundle ID.
  • A certificate appears in the developer account but signing fails on a new Mac; what is missing?
    Its private key. A certificate signs only when the keychain holds the key created with its signing request. Import the `.p12` exported from the original machine, or create a new certificate from this Mac.
  • Which Flutter-project file records the signing style?
    The Runner target's build settings in `ios/Runner.xcodeproj/project.pbxproj`, edited through Xcode's Signing & Capabilities tab. Nothing about signing lives in `pubspec.yaml`.

The certificate is a notary's seal, useless without the notary's own hand (the private key); the provisioning profile is the signed engagement letter saying which client, which documents and which extra powers that notary may use.

saying these in an interview costs you the question

  • The provisioning profile contains the private key used to sign.
  • An App Store provisioning profile lists every tester's device UDID.
  • Automatic signing cannot produce App Store builds; release needs manual signing.
  • One provisioning profile covers the app and all of its extensions.
  • A development certificate is enough to export an App Store IPA.
  • Signing settings for a Flutter iOS app are configured in pubspec.yaml.