Skip to content

Releasing for iOS

This page takes a Flutter app from Xcode signing setup to a build on the App Store that can receive Shorebird patches. Steps 1 and 2 are the same one-time setup any Flutter app needs. Step 3 builds the archive and IPA with shorebird release ios on a Mac, and step 4 uploads that build to App Store Connect.

Step 1: Configure bundle identifier and signing in Xcode

Section titled “Step 1: Configure bundle identifier and signing in Xcode”

Open the ios directory in Xcode:

open ios/Runner.xcworkspace
  1. Select the Runner project in the sidebar.
  2. Under the Signing & Capabilities tab, select your Team registered with the Apple Developer Program.
  3. Set a unique Bundle Identifier (e.g., com.example.myapp).
  4. Choose Automatically manage signing (recommended for standard release flows) or configure custom provisioning profiles.

shorebird release ios signs the IPA with these settings. If it stops with a signing or provisioning profile error, see Signing issues.

Step 2: Privacy manifests and permission descriptions

Section titled “Step 2: Privacy manifests and permission descriptions”

Apple requires explicit usage descriptions in ios/Runner/Info.plist for any hardware or sensitive APIs accessed by your app or third-party packages (see Apple Info.plist Keys):

<key>NSCameraUsageDescription</key>
<string>This app requires access to the camera to scan documents.</string>
<key>NSPhotoLibraryUsageDescription</key>
<string>This app requires access to your photo library to select a profile picture.</string>

App Store Connect also requires a privacy manifest (PrivacyInfo.xcprivacy) for apps that use required reason APIs, declaring why the app or its dependencies call them. Apple enforces this for every app submitted since May 1, 2024, whatever iOS version the app targets.

On a Mac with Xcode, build the release with Shorebird’s Flutter. This archives your app, exports a signed IPA, and uploads the compiled Dart code to Shorebird as the base that later patches apply to:

shorebird release ios

It writes the archive to build/ios/archive/ and the IPA to build/ios/ipa/, and prints the ways to upload them when it finishes.

shorebird release takes the common flutter build options, such as --flavor, --target, --build-name, --build-number and --dart-define, and passes any other flutter build argument you put after a -- separator. To sign with a specific certificate and provisioning profile, pass an --export-options-plist. See Create a Release for those, and run shorebird release --help for every option.

To obfuscate your Dart code, pass --obfuscate, and --split-debug-info with a directory to keep the symbol files in:

shorebird release ios --obfuscate --split-debug-info=build/symbols

Obfuscation on iOS needs a release built with Flutter 3.41.2 or later. Patches to an obfuscated release are obfuscated the same way automatically. See Code obfuscation and debug symbols.

If you sign the app in a later step, pass --no-codesign. Shorebird then builds only the .xcarchive in build/ios/archive/. Sign and upload that archive, without rebuilding it, by hand in Xcode (the Xcode tab below) or in CI with xcodebuild -exportArchive or fastlane. Whatever signs and uploads it must keep the build number; see the caution in step 4.

Upload the build from step 3 using one of these methods:

  1. Download the Transporter app from the Mac App Store.
  2. Drag and drop the .ipa file from build/ios/ipa/ into Transporter.
  3. Click Deliver to upload.
  1. TestFlight Internal Testing: Available immediately after processing for members of your App Store Connect team.
  2. TestFlight External Testing: Distribute builds to up to 10,000 external testers using TestFlight (requires a brief initial Beta App Review from Apple).
  3. App Store submission: Fill out App Store listing metadata, screenshots, age ratings, and privacy nutrition labels, then submit for App Review. Apple offers Phased Release for Automatic Updates (rolling out over 7 days).

The build you test in TestFlight is the same build you submit, so it receives the same patches. To try a patch on TestFlight devices before everyone gets it, see Testing patches.

When you change Dart code in this version later, ship the change as a patch instead of a new store build:

shorebird patch ios --track=stable

A change to native code, a plugin with native code, assets such as images and fonts, or the Flutter version needs a new store release: bump the version in pubspec.yaml, then repeat steps 3 and 4. iOS patches only install on physical devices, not the Simulator. See Create a Patch.

For background: a Flutter app that doesn’t use Shorebird builds its store release with flutter build ipa (which writes the same build/ios/archive/ and build/ios/ipa/ outputs) or by archiving in Xcode. A build made with the standard Flutter has no Shorebird updater, so it can never receive a patch. Once your app uses Shorebird, use shorebird release ios for every build you upload to App Store Connect.