How to Publish a Flutter App to the iOS App Store in 2026: 5 Problems to Check First

Coding Liquids blog cover featuring Sagnik Bhattacharya for How to Publish a Flutter App to the iOS App Store in 2026: 5 Problems to Check First, with App Store Connect panels, Xcode signing cards, Info.plist permission checks, and Flutter iOS release visuals.
Coding Liquids blog cover featuring Sagnik Bhattacharya for How to Publish a Flutter App to the iOS App Store in 2026: 5 Problems to Check First, with App Store Connect panels, Xcode signing cards, Info.plist permission checks, and Flutter iOS release visuals.

Publishing a Flutter app to the iOS App Store in 2026 is not difficult because Flutter is difficult. It is difficult because Apple publishing has a lot of small locks: Bundle IDs, signing certificates, provisioning profiles, Info.plist privacy strings, Xcode versions, app privacy answers, build numbers, and App Store Connect processing. Beginners often finish the app, run it happily on Android, then meet Apple paperwork at the finish line.

The Complete Flutter Guide course thumbnail

The Complete Flutter Guide: Build Android, iOS and Web apps

Go from scratch to building industry-standard apps with Riverpod, Firebase, animations, REST APIs, and more.

Get 85% off on Udemy

This tutorial gives you the practical path: the five publishing problems to check first, then a proper step-by-step release workflow from a Flutter project to TestFlight and App Review. It is written for developers who can build a Flutter app, but have not yet developed that quiet sixth sense for Apple signing errors.

Follow me on Instagram@sagnikteaches

Quick answer

To publish a Flutter app to the iOS App Store in 2026, use a Mac or macOS build runner with Xcode 26 or later, enrol in the Apple Developer Program, register an explicit Bundle ID, create an App Store Connect app record, configure signing and capabilities in Xcode, add the required Info.plist permission strings, build the release IPA with flutter build ipa, upload the build to App Store Connect, test it through TestFlight, complete metadata and privacy answers, then submit for App Review.

Connect on LinkedInSagnik Bhattacharya

The five problems to check before you build are: Bundle ID mismatch, missing Info.plist privacy strings, SwiftPM or CocoaPods confusion, unsupported Xcode or SDK versions, and broken signing or provisioning profiles. Most failed first submissions come from one of those five areas, not from Dart code.

Subscribe on YouTube@codingliquids

Before you start: the publishing checklist

Do this before opening App Store Connect. It saves hours because you will know whether the issue is your Flutter app, your Mac, your Apple account, or your project settings.

  • Mac or macOS runner: iOS release builds require Xcode. You can code on Windows, but the signing and upload path still needs macOS somewhere.
  • Apple Developer Program account: a free Apple ID can test on device, but App Store distribution needs a paid developer membership.
  • Xcode 26 or later for 2026 uploads: since 28 April 2026, iOS and iPadOS uploads to App Store Connect need to be built with Xcode 26 or later and the iOS or iPadOS 26 SDK or later.
  • Flutter project builds on iOS: run a release build locally before you fight App Store metadata.
  • Real app name, icon, screenshots, and privacy answers: Apple review is not just a binary upload.
flutter doctor -v
flutter pub get
flutter build ios --release --no-codesign

--no-codesign is useful as an early build check because it tells you whether Flutter and Xcode can compile the app before distribution signing enters the picture. It does not replace a signed archive.

Problem 1: Bundle ID mismatch

The Bundle ID is the app's identity in Apple's ecosystem. It usually looks like com.company.product. Flutter beginners often leave the default com.example.app, rename only the display name, then wonder why App Store Connect, Xcode, Firebase, push notifications, and provisioning profiles disagree.

Your Bundle ID must match across these places:

  • Apple Developer account - Certificates, Identifiers & Profiles.
  • App Store Connect - the app record you created.
  • Xcode - Runner target, General tab, Bundle Identifier.
  • Flutter iOS project - ios/Runner.xcodeproj and generated settings.
  • Third-party services - Firebase, deep links, sign in providers, push notification services, and payment providers.

For a new app, choose a Bundle ID you can keep for years. Changing it later can break associated services. For a client app, use the client's Apple team and organisation naming, not your personal test identifier.

# Useful checks from your Flutter project
grep -R "PRODUCT_BUNDLE_IDENTIFIER" ios/Runner.xcodeproj/project.pbxproj
open ios/Runner.xcworkspace

In Xcode, select Runner, then the Runner target, then General. Under Identity, confirm the Bundle Identifier is the exact explicit App ID registered in the Apple Developer portal.

Problem 2: Info.plist permission strings

Apple requires purpose strings for protected APIs. In Flutter, the confusing part is that a plugin can pull in native camera, microphone, location, photos, Bluetooth, speech, contacts, calendar, or Face ID code even when the permission request happens far from your Dart entry point. App Store Connect can reject the upload, or the app can crash when iOS tries to show a permission prompt and the required key is missing.

Open ios/Runner/Info.plist and add only the keys your app genuinely needs. The value should be clear and user-facing, not vague. "We need access" is weak. "Use the camera to scan receipts for your expense report" is better.

<key>NSCameraUsageDescription</key>
<string>Use the camera to scan receipts and attach photos to your reports.</string>
<key>NSMicrophoneUsageDescription</key>
<string>Use the microphone to record voice notes inside your tasks.</string>
<key>NSPhotoLibraryUsageDescription</key>
<string>Choose photos from your library to add them to your profile.</string>
<key>NSLocationWhenInUseUsageDescription</key>
<string>Show nearby stores while you are using the app.</string>

Do not blindly paste every privacy key into Info.plist. Apple can also ask questions about why you request capabilities you do not use. Map permissions to features and plugins. If you remove a plugin, remove stale permissions unless another feature still needs them.

Problem 3: Swift Package Manager vs CocoaPods

Swift Package Manager, often shortened to SwiftPM or SPM, is Apple's dependency manager. CocoaPods is the older dependency manager many Flutter iOS projects already use through Podfile and Podfile.lock. Flutter's Apple-platform tooling has been moving more SwiftPM-aware, but real projects can still include CocoaPods, plugin-specific native dependencies, and CI scripts that run pod install.

The mistake is choosing ideology over evidence. Do not say "CocoaPods is gone" and delete files because a headline made it sound modern. Also do not ignore SwiftPM if your current Flutter and plugin stack has started using it. Inspect your project.

  • If your project has ios/Podfile and ios/Podfile.lock, understand which plugins still depend on Pods.
  • If your generated Xcode workspace has Swift package references, verify they resolve on the same machine or CI runner that builds releases.
  • If you are upgrading Flutter near the release, read the Flutter 3.44 upgrade checklist and the Flutter 3.44 deep dive before changing dependency-management files.
  • Commit dependency lockfile changes deliberately so the release branch is reproducible.
flutter clean
flutter pub get
cd ios
pod install
cd ..
flutter build ios --release --no-codesign

If pod install fails, read the error before changing Ruby, CocoaPods, Flutter, and Xcode all at once. Common causes include an outdated local CocoaPods install, a plugin requiring a higher iOS deployment target, an Apple Silicon/Rosetta mismatch on older setups, or a corrupted Pods cache.

Problem 4: Unsupported Xcode or SDK version

Apple changes App Store upload requirements over time. In 2026, the key practical rule is simple: for iOS and iPadOS apps uploaded to App Store Connect after 28 April 2026, build with Xcode 26 or later and the iOS or iPadOS 26 SDK or later. This does not mean your app must require iOS 26 as the minimum user device version. Build SDK and deployment target are different things.

Think of it this way:

  • Build SDK: the Apple SDK version used by Xcode to build the app for App Store upload.
  • Deployment target: the oldest iOS version your app supports at runtime.
  • Flutter iOS deployment target: the minimum target your Flutter version and plugins can support.

In Xcode, check Runner target > Build Settings > iOS Deployment Target. The Flutter iOS release guide notes that Flutter supports iOS 13 and later, but your plugins might require a higher target. For example, a camera, maps, payments, or authentication plugin may force a newer target than a plain Flutter UI app.

xcodebuild -version
flutter --version
flutter doctor -v

If App Store Connect says the SDK is unsupported, do not try to fix it by editing pubspec.yaml. Update the Xcode used for the archive, including the Xcode selected by CI. Many CI failures happen because the developer's Mac is updated but the hosted runner is still pinned to an older Xcode image.

Problem 5: Signing and provisioning profiles

Signing proves who built the app. A provisioning profile tells Apple which app, certificate, capabilities, and distribution method belong together. Flutter can build the Dart and native code, but Xcode still handles the Apple signing world.

For most beginner apps, start with Xcode automatic signing:

  1. Open ios/Runner.xcworkspace, not ios/Runner.xcodeproj.
  2. Select the Runner target.
  3. Open Signing & Capabilities.
  4. Tick Automatically manage signing.
  5. Select the Apple Developer team that owns the app.
  6. Confirm the Bundle Identifier matches the explicit App ID.
  7. Add the required capabilities, such as Push Notifications, Associated Domains, Sign in with Apple, or In-App Purchase.

Manual signing is better for larger teams and CI, but it requires discipline. The App Store provisioning profile must match the explicit App ID, distribution certificate, and capabilities. If you regenerate certificates or change team membership, CI secrets and profiles may also need updates.

Common signing errors usually mean one of four things: wrong Apple team, Bundle ID mismatch, missing capability in the App ID, or a stale provisioning profile. Rebuild profiles after changing capabilities.

Step 1: Create the App Store Connect app record

Once the Bundle ID is registered, create the app in App Store Connect. This app record is where Apple stores the name, SKU, Bundle ID, platform, screenshots, pricing, privacy answers, TestFlight builds, and review submission.

  1. Open App Store Connect.
  2. Go to Apps and choose New App.
  3. Select iOS as the platform.
  4. Enter the app name, primary language, Bundle ID, and SKU.
  5. Create the record and confirm it appears under My Apps.

The SKU is an internal identifier. Users do not see it. Use something stable, such as com.company.product.ios or an internal product code.

Step 2: Review Xcode project settings

Flutter creates the iOS runner project for you, but you still need to review the Apple-specific settings. Open the workspace, not the project file, because the workspace includes dependency integration.

open ios/Runner.xcworkspace

Review these settings in the Runner target:

  • Display Name: the name users see under the icon.
  • Bundle Identifier: the exact explicit App ID registered with Apple.
  • Version: user-facing app version, such as 1.0.0.
  • Build: internal build number, such as 1, 2, or your CI build number.
  • Deployment target: the minimum iOS version supported by your app and plugins.
  • Signing team: the Apple team that owns the App Store Connect record.
  • Capabilities: push, associated domains, app groups, iCloud, in-app purchases, or sign-in features your app genuinely uses.

If you are building a serious app, connect this review to your architecture and testing process. The Flutter app architecture guide and Flutter testing strategy will help you keep release changes reviewable instead of mixing signing fixes with feature rewrites.

Step 3: Set the Flutter version and build number

Flutter stores the normal version in pubspec.yaml using the version: name+number pattern.

version: 1.0.0+1

For iOS, the version name maps to CFBundleShortVersionString. The build number maps to CFBundleVersion. App Store Connect requires each upload for the same app version to have a unique build number. If you upload 1.0.0+1 and then need to fix something, the next upload should be 1.0.0+2, not the same build again.

flutter build ipa --release --build-name=1.0.0 --build-number=2

Use CI build numbers when possible. Humans are excellent at forgetting to increment build numbers five minutes before a release.

Step 4: Add app icons and launch assets

The default Flutter icon is fine for a simulator. It is not fine for App Review. Replace the placeholder iOS icons in ios/Runner/Assets.xcassets. Make sure the App Store icon has no transparency and matches Apple's current icon requirements.

Also check your launch screen. A launch screen should be simple and quick. Do not use it as a fake loading screen for long backend work. If the app takes time to initialise, optimise startup and show a real loading state after the app has launched. The Flutter performance guide covers the profiling mindset for startup and jank.

Step 5: Build a release IPA

Now build the signed release package. Make sure you are on a clean release branch and that unrelated feature work is not mixed into the publishing change.

git status
flutter clean
flutter pub get
flutter test
flutter build ipa --release

flutter build ipa creates an Xcode archive under build/ios/archive/ and an IPA under build/ios/ipa/. If signing is configured correctly, this is the file you can upload to App Store Connect.

If you use obfuscation, keep the split debug info output somewhere safe. Without the symbols, crash diagnosis becomes much harder.

flutter build ipa --release --obfuscate --split-debug-info=build/debug-info/ios

Do not enable obfuscation for the first time during a rushed release unless you have tested crash reporting and symbol handling. A boring, debuggable first release is better than a mysterious one.

Step 6: Validate and upload the build

You have three common upload paths:

  • Xcode Organizer: open the archive, validate it, then distribute to App Store Connect.
  • Transporter: drag the IPA into Apple's Transporter app and upload.
  • Command line or CI: use App Store Connect API keys with tools that can publish the IPA.

For a first app, Xcode Organizer is the friendliest because validation messages are visible. Open the archive from build/ios/archive/, validate, fix any reported issue, then upload. After upload, App Store Connect processes the build before it appears for TestFlight or App Review.

If validation fails, do not keep uploading the same broken IPA. Fix the cause, increment the build number if a previous upload reached App Store Connect, rebuild, and upload again.

Step 7: Use TestFlight before App Review

TestFlight is where you catch the embarrassing release-only problems: sign-in callbacks, network configuration, push tokens, camera permissions, iPad layout, dark mode, deep links, in-app purchases, and real device behaviour. Do not skip it because the app works in debug mode.

At minimum, test these flows:

  • Fresh install and first launch.
  • Sign up, sign in, sign out, and session restore.
  • Every permission prompt used by the app.
  • Payments, subscriptions, or in-app purchases if present.
  • Deep links, push notifications, and universal links if present.
  • iPhone and iPad layouts if the app supports both.
  • Offline and slow-network behaviour for important screens.

Flutter widget tests will not catch every Apple integration issue. Use them, but also do a TestFlight smoke test. The combination is what protects you.

Step 8: Complete metadata, privacy, and compliance

App Review sees more than your IPA. Complete the product page carefully:

  • App name, subtitle, category, age rating, and description.
  • Screenshots for required device sizes.
  • Support URL and privacy policy URL.
  • App privacy answers that match the data your app and SDKs collect.
  • Export compliance answers, especially if you use encryption, authentication, HTTPS, or crypto-related features.
  • Review notes and demo credentials if the app requires sign-in.

Be specific in review notes. If the reviewer needs to log in, give a test account. If a feature needs a location, explain how to reach it. If the app uses hardware, subscriptions, or role-based access, make the review path obvious.

Step 9: Submit for App Review

When the build has passed TestFlight checks and metadata is complete, choose the build in the app version page and submit for review. Apple may approve it, reject it with a specific guideline issue, ask for more information, or flag a binary/metadata mismatch.

If rejected, read the message slowly. Beginners often jump into code when the issue is actually metadata, privacy, sign-in credentials, or a missing review explanation. Fix the smallest thing that addresses the issue, then resubmit.

Troubleshooting common Flutter iOS publishing errors

Error or symptomLikely causeWhat to check
App Store Connect cannot find the app recordBundle ID mismatchApple App ID, App Store Connect app record, Xcode Runner target
Missing purpose string or ITMS privacy errorInfo.plist lacks a required permission keyCamera, microphone, photos, location, contacts, Bluetooth, speech, Face ID
Unsupported SDK or Xcode versionArchive was built with an old Xcodexcodebuild -version locally and in CI
No matching provisioning profileWrong team, certificate, Bundle ID, or capabilitySigning & Capabilities, App ID capabilities, distribution profile
Build number already usedSame CFBundleVersion uploaded beforeIncrement version: 1.0.0+N or pass --build-number
Pod install failsCocoaPods, plugin, deployment target, or Ruby environment issuePodfile, plugin docs, iOS deployment target, CocoaPods version
App crashes when permission opensMissing or mismatched Info.plist purpose stringExact protected API used by plugin or native code
Push notifications fail in TestFlightCapability or APNs configuration mismatchAssociated App ID capability, Firebase/APNs key, entitlements

Worked example: first release of a Flutter booking app

Imagine a small Flutter booking app. It has email sign-in, map search, image upload for venue photos, and push notifications. The Android app is already on internal testing. The iOS release fails on the first serious attempt.

The team checks the five problem areas. Bundle ID: the app uses com.example.booking in Xcode but com.client.booking in App Store Connect. Fix one. Info.plist: the app uses location and photos, but only has a camera string. Add the correct purpose strings. Dependencies: CocoaPods resolves locally, but CI uses an old CocoaPods version. Update the runner image. Xcode: CI is pinned to old Xcode, so the upload SDK is wrong. Update to Xcode 26. Signing: push notifications were enabled in Firebase but not in the Apple App ID capabilities. Enable capability and regenerate the profile.

No single fix is glamorous. Together they turn a failed upload into a build that reaches TestFlight. That is the real lesson: iOS publishing is usually a chain of small alignment checks.

Beginner mistakes to avoid

  • Building on Android only and assuming iOS will work on release day.
  • Using com.example until the final week.
  • Adding every Info.plist permission key instead of matching real features.
  • Deleting CocoaPods files because SwiftPM sounds newer.
  • Updating Flutter, Xcode, plugins, Gradle files, and app architecture in one release branch.
  • Skipping TestFlight because debug mode worked.
  • Uploading without a privacy policy or working test credentials.
  • Forgetting to increment the iOS build number after a failed upload that reached App Store Connect.

When to use CI for iOS publishing

Use a local Mac for your first learning pass if you can. It helps you see Xcode, signing, and App Store Connect directly. Move to CI when the release becomes repetitive or team-owned.

A good CI setup stores App Store Connect API credentials securely, uses a known Xcode image, fetches signing files, runs tests, builds the IPA, uploads to App Store Connect, and records the Flutter, Dart, Xcode, build number, and git commit used for the release. That audit trail matters when a client asks, "Which build did we send?"

Keep the CI release path small. A release job should not silently upgrade packages or mutate project settings. It should build the code you reviewed.

Where this fits in your Flutter learning path

Publishing is the final exam for your Flutter project structure. If the app is well organised, tested, and has clear platform boundaries, the App Store path is much less stressful. If everything is tangled, signing and metadata problems feel worse because you are also afraid of breaking app logic.

Use the Flutter development hub as your map. For core learning, see Best Flutter Courses in 2026. If your release is slowed by architecture problems, read Flutter App Architecture in 2026. If you are missing test confidence, read Flutter Testing Strategy in 2026. If you are embedding Flutter in a native host app, read Add Flutter to an Existing App.

Final pre-submit checklist

  • Bundle ID matches Apple Developer, App Store Connect, Xcode, Firebase, and any sign-in providers.
  • App Store app record exists and uses the correct platform and Bundle ID.
  • Runner target uses the right Apple team and capabilities.
  • Info.plist contains only the required purpose strings, with clear user-facing explanations.
  • Xcode 26 or later is used for the archive in 2026 upload workflows.
  • Flutter, Dart, Xcode, and dependency versions are recorded.
  • flutter test passes for the tests you have.
  • flutter build ipa --release creates a signed IPA.
  • Build number is unique for the selected app version.
  • TestFlight smoke test passes on real devices.
  • Privacy policy, screenshots, app privacy, review notes, and demo credentials are complete.

FAQ

Can I publish a Flutter iOS app without a Mac?

You need macOS with Xcode somewhere in the workflow. That can be your own Mac, a cloud macOS runner, or a managed build service. Windows can be fine for writing Dart code, but it cannot replace the Apple build and signing toolchain for App Store distribution.

Should I use flutter build ios or flutter build ipa?

Use flutter build ios for compiling an iOS release build, especially with --no-codesign as an early check. Use flutter build ipa when you are preparing the archive and IPA for App Store Connect upload.

Does Swift Package Manager replace CocoaPods for every Flutter app?

No. Treat SwiftPM and CocoaPods as project checks, not slogans. Flutter's Apple tooling includes SwiftPM work, but real Flutter apps can still have CocoaPods files and plugin workflows. Inspect the generated iOS project, plugin requirements, and CI logs before deleting anything.

Why does my iOS app work in debug but fail App Store upload?

Debug mode does not prove distribution signing, App Store SDK requirements, privacy metadata, build number uniqueness, app icons, or archive validation. A signed release archive exercises a different part of the toolchain.

How long does App Store review take?

It varies. Plan for review time instead of publishing at the last minute. The part you control is quality of submission: clear metadata, working demo credentials, accurate privacy answers, a tested build, and no avoidable signing or permission mistakes.

Related Flutter tutorials