Codemagic iOS ad hoc builds for Flutter: ios_signing and use-profiles
· 5 min read
An App Store signed .ipa from Codemagic is fine for TestFlight, but it will not install from a link on a tester's iPhone. For that you need an ad hoc build (or development, or enterprise) whose profile lists the tester's device. Codemagic can fetch or make that profile for you. This guide shows the workflow for a Flutter app, the one flag that trips people up, and what to do when a new iPhone joins. Codemagic facts are from docs.codemagic.io as read on November 5, 2026.
What you need first
- An App Store Connect API key added to Codemagic. Codemagic uses it to fetch or make certificates and profiles.
- The bundle id of the app, for example
io.acme.notes, registered in your Apple Developer account. - The UDIDs of the test iPhones registered on Apple. Testers can find theirs with the free UDID finder, no cable or Mac needed.
The workflow
workflows:
ios-adhoc:
name: iOS ad hoc
environment:
groups: [appforshare]
flutter: stable
ios_signing:
distribution_type: ad_hoc
bundle_identifier: io.acme.notes
scripts:
- name: Get packages
script: flutter pub get
- name: Use profiles
script: xcode-project use-profiles
- name: Build ipa
script: flutter build ipa --release --export-options-plist=/Users/builder/export_options.plist
- name: Mark success
script: touch ~/SUCCESS
artifacts:
- build/ios/ipa/*.ipa
What each part does, per Codemagic's iOS code signing docs:
ios_signingtells Codemagic which kind of profile to get.distribution_typecan beapp_store,ad_hoc,developmentorenterprise. With an API key, Codemagic fetches the matching certificate and profile forbundle_identifier.xcode-project use-profilesapplies those profiles to the Xcode project and writes/Users/builder/export_options.plist.flutter build ipa --export-options-plist=...builds and exports with that file, so the export uses the ad hoc profile.
Codemagic's own Flutter quick start says: "When using a third party app distribution service such as Firebase App Distribution, set the distribution_type to ad_hoc." The same goes for any install link service.
The --export-method trap
Many Flutter guides, including ours for local builds, say flutter build ipa --export-method ad-hoc. On Codemagic, do not combine that flag with --export-options-plist. Flutter does not accept both together, and using --export-method alone skips the export options file Codemagic wrote, so the export may not use the profile Codemagic prepared. Keep the plist and leave the method to ios_signing.
Related: since Xcode 15.4, the export method names in an options file changed from ad-hoc to release-testing and from development to debugging. When use-profiles writes the file for you, you do not have to care.
Apps with extensions
A widget, notification service or share extension has its own bundle id and needs its own profile. If the build fails with a profile error that names an extension, that target is missing a profile: check Codemagic's signing docs for how to sign more than one bundle id, and read which target the error names. Our guide to "No matching profiles found" on Codemagic walks through the usual causes.
Check the .ipa before you send it
Download the .ipa from the build and drop it into the IPA inspector, or drop the .mobileprovision and the .ipa into the provisioning profile inspector. Both run in your browser and never upload the file. Look for three things:
- The type says Ad Hoc, not App Store.
- The profile has not expired.
- Each tester's UDID is in the profile.
If a UDID is missing, the iPhone will refuse the install, usually with "integrity could not be verified".
Sharing the build
Add a publishing script that uploads the file to an install page. Codemagic runs publishing scripts even after a failed build, so check the ~/SUCCESS file the last build script wrote:
publishing:
scripts:
- name: Share with testers
script: |
if [ -f ~/SUCCESS ]; then
npx --yes appforshare upload build/ios/ipa \
--notes "$(git log -1 --pretty=%B)" --channel beta
fi
APPFORSHARE_TOKEN goes in the appforshare group as a Secure variable (upload tokens are part of AppForShare Pro and Team). Testers then open an install page with an Install button on the iPhone. More on why this beats the artifact link is in Share a Codemagic build with testers.
When a new tester joins
A new iPhone means a new profile and a new build. Nothing changes an .ipa you already exported. The steps:
| Step | With Codemagic only | With AppForShare added |
|---|---|---|
| Get the UDID | Device registration link by email (a team feature) | Onboarding link per iOS project; the tester installs a profile, no account (Pro) |
| Register on Apple | Codemagic adds the device to the Apple Developer Portal | An Admin registers it in one click with your App Store Connect API key (Pro) |
| New profile | use-profiles on the next build | Same, or AppForShare remakes the ad hoc profile on Apple |
| New build | Run the workflow again | Run the workflow again; the tester gets one email when that build is uploaded |
Apple allows 100 devices per device type per membership year, and disabled devices keep their slot until renewal. Count before you add a whole team.
Common questions
Can I build ad hoc without a Mac?
Yes, on Codemagic: its Macs build and sign the app. That is one of the main reasons Flutter developers use it.
Should I use development instead of ad hoc?
Development builds work for your own team's devices, but testers on iOS 16 and later must turn on Developer Mode. For people outside the team, ad hoc is easier.
Can the same workflow make a TestFlight build?
Use a second workflow with distribution_type: app_store. That .ipa goes to TestFlight, either through Codemagic's publishing or by uploading it to AppForShare and letting an Admin send it (Pro and Team). See Codemagic TestFlight publishing errors.
The complete setup with Android and the Workflow Editor is in Use AppForShare with Codemagic. To see the install page your testers would get, create a free account and upload an ad hoc .ipa.


