skills/willsigmon/sigstack/TestFlight Expert

TestFlight Expert

SKILL.md

TestFlight Expert

Distribute beta builds and collect feedback through Apple's TestFlight.

TestFlight Overview

  • Internal Testing: Up to 100 testers (no review)
  • External Testing: Up to 10,000 testers (requires review)
  • Build Expiry: 90 days
  • Feedback: Screenshots and crash reports

Fastlane Upload

Basic Upload

# Fastfile
lane :beta do
  build_app(scheme: "App")
  upload_to_testflight(
    skip_waiting_for_build_processing: true
  )
end

With Changelog

lane :beta do
  build_app(scheme: "App")
  upload_to_testflight(
    changelog: "Bug fixes and improvements",
    distribute_external: true,
    groups: ["Beta Testers"]
  )
end

App Store Connect API

Setup

# Create API key in App Store Connect
# Users and Access → Keys → App Store Connect API

# Store credentials
export APP_STORE_CONNECT_API_KEY_ID="XXXXXXXXXX"
export APP_STORE_CONNECT_ISSUER_ID="xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
export APP_STORE_CONNECT_API_KEY_PATH="~/.appstore/AuthKey.p8"

Fastlane Config

# Appfile
app_store_connect_api_key(
  key_id: ENV["APP_STORE_CONNECT_API_KEY_ID"],
  issuer_id: ENV["APP_STORE_CONNECT_ISSUER_ID"],
  key_filepath: ENV["APP_STORE_CONNECT_API_KEY_PATH"]
)

Managing Testers

Add Internal Tester

# Must be App Store Connect user
# Add manually in App Store Connect

Add External Group

lane :add_testers do
  upload_to_testflight(
    groups: ["Beta Testers", "VIP Testers"],
    distribute_external: true
  )
end

CLI Tester Management

# Using app-store-connect-cli
asc testflight add-tester \
  --email "tester@example.com" \
  --group "Beta Testers" \
  --app-id 123456789

CI/CD Integration

GitHub Actions

name: TestFlight
on:
  push:
    branches: [main]

jobs:
  testflight:
    runs-on: macos-14
    steps:
      - uses: actions/checkout@v4

      - name: Setup Signing
        uses: apple-actions/import-codesign-certs@v2
        with:
          p12-file-base64: ${{ secrets.CERTIFICATES_P12 }}
          p12-password: ${{ secrets.CERTIFICATES_PASSWORD }}

      - name: Build & Upload
        run: |
          bundle exec fastlane beta
        env:
          APP_STORE_CONNECT_API_KEY_ID: ${{ secrets.ASC_KEY_ID }}
          APP_STORE_CONNECT_ISSUER_ID: ${{ secrets.ASC_ISSUER_ID }}
          APP_STORE_CONNECT_API_KEY: ${{ secrets.ASC_KEY }}

Build Processing

Wait for Processing

lane :beta do
  build_app(scheme: "App")
  upload_to_testflight(
    skip_waiting_for_build_processing: false,
    wait_processing_timeout_duration: 3600
  )
  # Build is ready for testing
end

Skip Waiting

lane :beta do
  build_app(scheme: "App")
  upload_to_testflight(
    skip_waiting_for_build_processing: true
  )
  # Continue without waiting (faster CI)
end

Beta Review Tips

What Gets Reviewed

  • First build of new app
  • Major version changes
  • Significant feature additions

Speed Up Review

  • Complete app description
  • Add test account credentials
  • Clear beta release notes
  • No placeholder content

Typical Review Time

  • 24-48 hours for external builds
  • Internal builds: No review needed

Collecting Feedback

In-App Feedback

import StoreKit

// Prompt for beta feedback
SKStoreReviewController.requestReview()

TestFlight Feedback

  • Users shake device to send feedback
  • Includes screenshot + notes
  • Crash reports auto-collected

Version Management

lane :beta do
  # Auto-increment build number
  increment_build_number(
    build_number: latest_testflight_build_number + 1
  )

  build_app(scheme: "App")
  upload_to_testflight
end

Use when: Beta distribution, tester management, CI upload automation

Weekly Installs
0
GitHub Stars
7
First Seen
Jan 1, 1970