Skip to content

TestFlight Deployment Guide

Complete guide for deploying Nutri-E to TestFlight via Xcode Cloud.

Quick Reference

App Store Connect: https://appstoreconnect.apple.com TestFlight Builds: Apps → Nutri-E → TestFlight → iOS Xcode Cloud: Apps → Nutri-E → Xcode Cloud

Current Setup (October 2025)

Production Status

  • Live Version: 1.0.3 (Build 5 in Xcode Cloud / Build 29 locally)
  • Deployment Method: Xcode Cloud (automatic on main branch pushes)
  • Testing Group: InternalTesters

Build Numbering Note: Xcode Cloud maintains separate build numbering from local Xcode: - Local Xcode project: CURRENT_PROJECT_VERSION = 29 - Xcode Cloud builds: Increments separately (currently Build 5) - Both represent the same app version (1.0.3) - See Apple's Xcode Cloud docs for details

Xcode Cloud Workflow

Workflow Name: Default

Trigger: - Branch: main - Conditions: Any changes (TODO: Add ios/** filter)

Actions: - Archive iOS app - Upload to App Store Connect

Post-Actions: - ✅ Automatic TestFlight Internal Testing distribution (Working as of Oct 24, 2025) - TODO: Add email notifications on failure

Deployment Process

Automatic Deployment (Xcode Cloud)

When you push iOS changes to main:

git checkout main
git pull origin main
# Make your changes...
git add .
git commit -m "feat: Your feature description"
git push origin main

What happens: 1. Xcode Cloud detects push to main 2. Builds and archives iOS app (Release configuration) 3. Uploads to App Store Connect 4. Processing takes 5-15 minutes 5. Automatically distributes to InternalTesters (Post-Action configured) 6. Build appears in TestFlight app automatically

That's it! No manual steps required. Just push to main and wait for TestFlight notification.

Version Bumping

Before deploying:

Check current version:

cd ios/Nutri-E
agvtool what-marketing-version
agvtool what-version

For bug fixes (patch):

# 1.0.3 Build 5 → 1.0.3 Build 6
agvtool next-version -all

For new features (minor):

# 1.0.3 → 1.1.0 Build 1
agvtool new-marketing-version 1.1.0
agvtool new-version -all 1

For breaking changes (major):

# 1.x.x → 2.0.0 Build 1
agvtool new-marketing-version 2.0.0
agvtool new-version -all 1

Monitoring Builds

Option 1: App Store Connect (Web)

  1. Open https://appstoreconnect.apple.com
  2. Navigate: Apps → Nutri-E → Xcode Cloud
  3. View all builds with status: Success, Failed, In Progress
  4. Click any build for detailed logs
  1. In Xcode, open Report Navigator (⌘9)
  2. Look for "Xcode Cloud" section
  3. All builds show with real-time status
  4. Click any build for full logs

Option 3: Email Notifications

  • Configure in Xcode Cloud Post-Actions
  • "Send Email" action on failure
  • Include build logs

Troubleshooting

Build Not Appearing in TestFlight

Problem: Build uploaded but not in TestFlight app

Solutions: 1. Check if build is still "Processing" (wait 5-15 minutes) 2. Verify build was added to InternalTesters group 3. Pull to refresh in TestFlight app 4. Check if Post-Action is configured for automatic distribution

Version Rejection Error

Problem: "This bundle is invalid. The value for key CFBundleShortVersionString [X.X.X]..."

Solution: Version train is closed, bump version:

cd ios/Nutri-E
agvtool new-marketing-version 1.0.4  # Or next version
git add Nutri-E.xcodeproj/project.pbxproj
git commit -m "chore: Bump version to 1.0.4 for TestFlight"
git push origin main

Build Fails in Xcode Cloud

Problem: Build fails with code signing or compilation errors

Steps: 1. Check build logs in App Store Connect → Xcode Cloud 2. Look for specific error message 3. Fix locally and test:

cd ios/Nutri-E
xcodebuild clean build \
  -project Nutri-E.xcodeproj \
  -scheme Nutri-E \
  -configuration Release
4. Push fix to main after local verification

Code Signing Issues

Problem: "No signing certificate..." or "No provisioning profile..."

Solution: Verify certificates in App Store Connect: 1. Settings → Certificates 2. Ensure "Apple Distribution" certificate is valid 3. Ensure App Store provisioning profile exists for bundle ID 4. Xcode Cloud uses App Store Connect's signing assets

Code Signing Setup

Required Certificates

  • Type: Apple Distribution
  • Team: V9GL74NMBR (Invotek AS)
  • App ID: no.invotek.Nutri-E

Provisioning Profile

  • Type: App Store
  • Bundle ID: no.invotek.Nutri-E
  • Devices: Not required (App Store profiles support all devices)

Xcode Cloud Signing

Xcode Cloud uses automatic signing with App Store Connect assets: - No need to upload certificates to GitHub - No need for manual provisioning profile management - Apple handles signing automatically

App Store Submission

When you're ready to submit to the App Store (not just TestFlight), follow these steps.

Prerequisites

Before submitting: 1. All TestFlight testing is complete 2. App metadata is prepared in App Store Connect: - Screenshots for all required device sizes - App description, keywords, support URL - Privacy policy URL - Age rating questionnaire completed 3. Version number is correct (can't reuse rejected versions)

Step 1: Archive in Xcode (Local Build)

Xcode Cloud builds can be promoted directly to the App Store from TestFlight. However, if you need a local archive:

cd ios/Nutri-E

# Clean build folder
xcodebuild clean -project Nutri-E.xcodeproj -scheme Nutri-E

# Archive for distribution
xcodebuild archive \
  -project Nutri-E.xcodeproj \
  -scheme Nutri-E \
  -destination 'generic/platform=iOS' \
  -archivePath ./build/Nutri-E.xcarchive

Or in Xcode GUI: 1. Select "Any iOS Device" as destination 2. Product → Archive 3. Wait for archive to complete (~5 minutes) 4. Organizer window opens automatically

Step 2: Upload to App Store Connect

Option A: Promote Xcode Cloud Build (Recommended)

If the build is already in TestFlight via Xcode Cloud: 1. Go to App Store Connect → Apps → Nutri-E → App Store tab 2. Click the version you're preparing (e.g., 1.0.4) 3. In "Build" section, click "+" to select a build 4. Choose the TestFlight build to promote 5. The build is now ready for App Store submission

Option B: Upload Local Archive

From Xcode Organizer: 1. Select the archive 2. Click "Distribute App" 3. Select "App Store Connect" → Next 4. Select "Upload" → Next 5. Choose signing options (usually "Automatically manage signing") 6. Review and Upload 7. Wait for upload (~5 minutes) and processing (~10-30 minutes)

Step 3: Submit in App Store Connect

  1. Open App Store Connect: https://appstoreconnect.apple.com
  2. Navigate: Apps → Nutri-E → App Store tab
  3. Select Version: Click on the version (e.g., 1.0.4)
  4. Complete Required Fields:
  5. What's New in This Version (release notes)
  6. Build (select uploaded/promoted build)
  7. App Review Information (contact info, demo account if needed)
  8. Review all sections for completeness (green checkmarks)
  9. Click "Add for Review"
  10. Click "Submit to App Review"

Step 4: Wait for Review

  • Review time: Usually 24-48 hours (can be faster or longer)
  • Status updates: Appear in App Store Connect and email notifications
  • Possible outcomes:
  • Approved: Ready for release (manual or automatic based on settings)
  • Rejected: Review rejection notes provided, fix and resubmit
  • In Review: Being actively reviewed

Common Rejection Reasons

Rejection Solution
ITMS-90186: "train is closed" Version was already rejected; bump to next version
Guideline 2.1: Crashes or bugs Fix bugs, test thoroughly before resubmitting
Guideline 4.2: Minimum functionality Ensure app provides meaningful user experience
Guideline 5.1.1: Privacy Update privacy policy, data collection declarations
Metadata Rejected Fix screenshots, descriptions, or keywords

Troubleshooting

"This version has already been submitted" - The version train is closed for this marketing version - Solution: Bump version number (e.g., 1.0.3 → 1.0.4)

cd ios/Nutri-E
agvtool new-marketing-version 1.0.4
agvtool new-version -all 1

Build not appearing in App Store Connect - Processing can take 10-30 minutes - Check email for any processing errors from Apple - Verify build uploaded successfully in Xcode Organizer

"Missing compliance" warning - Required for apps using encryption - Most apps can declare "No" for export compliance - Or add ITSAppUsesNonExemptEncryption=NO to Info.plist

Future Improvements

Optional: Add File Filters

1. Add File Filters (Optional): - Configure "Start Conditions" - Add "File & Folder Changes" condition - Pattern: ios/** - Prevents builds on non-iOS changes - Saves Xcode Cloud build minutes

2. Add Failure Notifications (Optional): - Add "Send Email" post-action - Condition: On Failure - Include build logs

History: GitHub Actions Attempts (October 2025)

We initially tried GitHub Actions for TestFlight deployment but encountered persistent issues:

Issue #102: macos-15 runner simulator runtime errors - Error: "No simulator runtime version... available to use with iphonesimulator SDK" - Root cause: actool validating simulator runtimes even for device builds - Attempted 8 different fixes (PRs #93-#101) - all failed - Decision: Pivoted to Xcode Cloud (user's original suggestion)

GitHub Actions workflow preserved for reference: - .github/workflows/deploy-testflight.yml - DO NOT USE - .github/workflows/test.yml - iOS archive build test (works, useful for PRs)

  • CLAUDE.md - Full project architecture and services
  • QUICK_REFERENCE.md - Common commands and daily workflows
  • .github/WORKFLOW_GUIDE.md - Git workflow and branch protection
  • CLOUDFLARE_ENVIRONMENTS.md - Worker deployment (separate from iOS)

Support

Apple Resources: - App Store Connect: https://appstoreconnect.apple.com - Xcode Cloud Docs: https://developer.apple.com/xcode-cloud/ - TestFlight Docs: https://developer.apple.com/testflight/

Internal: - Issues: https://github.com/Stig-Johnny/nutri-e/issues - PRs: https://github.com/Stig-Johnny/nutri-e/pulls