Guida - Automazione

Fastlane + Mac server dedicato: guida completa all'automazione

Automatizza l'intero processo di rilascio iOS -- build, code signing, screenshot e deployment su TestFlight -- usando Fastlane su un Mac Mini M4 server dedicato.

35 min di lettura Aggiornato a marzo 2026

1. Perché Fastlane su un Mac dedicato?

Fastlane è lo strumento di automazione standard per lo sviluppo iOS e Android. Gestisce tutto, dal code signing al deployment su TestFlight. Eseguire Fastlane su un Mac Mini M4 dedicato ti offre:

Keychain persistente

I certificati di code signing persistono tra le esecuzioni. Nessun bisogno di importarli/esportarli a ogni build.

Build incrementali veloci

I DerivedData persistono, quindi fastlane build richiede 2-4 minuti invece di oltre 15.

Screenshot dal simulatore

Esegui fastlane snapshot con simulatori reali per tutte le dimensioni dei dispositivi.

Deploy affidabili

Un ambiente stabile e dedicato significa meno deployment instabili e meno problemi di code signing.

2. Installa Fastlane

Collegati al tuo Mac Mini M4 via SSH e installa Fastlane. Consigliamo Homebrew per la configurazione più semplice:

Opzione A: installazione tramite Homebrew (consigliata)

# Install Homebrew (if not already installed)
/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"
echo 'eval "$(/opt/homebrew/bin/brew shellenv)"' >> ~/.zprofile
eval "$(/opt/homebrew/bin/brew shellenv)"

# Install Fastlane
brew install fastlane

# Verify installation
fastlane --version
# fastlane 2.225.0

Opzione B: installazione tramite RubyGems

# Use the system Ruby or install rbenv for version management
gem install fastlane -NV

# Or with Bundler (recommended for team consistency):
# Create a Gemfile in your project root
cat > Gemfile <<'EOF'
source "https://rubygems.org"

gem "fastlane"
gem "cocoapods"  # if using CocoaPods
EOF

bundle install

Inizializza Fastlane nel tuo progetto

# Navigate to your project directory
cd /path/to/your/ios-project

# Initialize Fastlane
fastlane init

# Choose option 4: "Manual setup"
# This creates the fastlane/ directory with Appfile and Fastfile

3. Configura Match per il code signing

Fastlane Match archivia i tuoi certificati di code signing e i provisioning profile in un repository Git privato o in un cloud storage. Questo garantisce che tutte le macchine (e i membri del team) usino la stessa identità di firma.

Inizializza Match

# Initialize match (choose "git" for storage)
fastlane match init

# This creates fastlane/Matchfile

Configura il Matchfile

# fastlane/Matchfile

git_url("https://github.com/your-org/ios-certificates.git")

storage_mode("git")

type("appstore")  # default type, can be overridden per lane

app_identifier(["com.yourcompany.myapp"])
username("your-apple-id@example.com")

# For CI environments, use App Store Connect API key instead of username/password
# api_key_path("fastlane/AuthKey.json")

Genera i certificati

# Generate development certificates and profiles
fastlane match development

# Generate App Store distribution certificates and profiles
fastlane match appstore

# For ad-hoc distribution
fastlane match adhoc

# On CI, use readonly mode to avoid accidentally creating new certs
fastlane match appstore --readonly

4. Crea il Fastfile (lane di build, test, deploy)

Ecco un Fastfile completo con le lane per compilare, testare e distribuire la tua app iOS:

# fastlane/Fastfile

default_platform(:ios)

platform :ios do

  # ---- SHARED ----

  before_all do
    setup_ci if ENV['CI']  # Configures keychain for CI environments
  end

  # ---- BUILD ----

  desc "Build the app for testing"
  lane :build do
    match(type: "development", readonly: true)

    build_app(
      workspace: "MyApp.xcworkspace",
      scheme: "MyApp",
      configuration: "Debug",
      destination: "generic/platform=iOS Simulator",
      derived_data_path: "DerivedData",
      skip_archive: true,
      skip_codesigning: true
    )
  end

  # ---- TEST ----

  desc "Run all unit and UI tests"
  lane :test do
    run_tests(
      workspace: "MyApp.xcworkspace",
      scheme: "MyApp",
      devices: ["iPhone 16 Pro"],
      derived_data_path: "DerivedData",
      result_bundle: true,
      output_directory: "fastlane/test_results",
      parallel_testing: true,
      concurrent_workers: 4
    )
  end

  # ---- BETA ----

  desc "Build and push a new beta to TestFlight"
  lane :beta do
    # Ensure we are on a clean git state
    ensure_git_status_clean

    # Fetch App Store certificates
    match(type: "appstore", readonly: true)

    # Increment build number
    increment_build_number(
      build_number: latest_testflight_build_number + 1
    )

    # Build the app
    build_app(
      workspace: "MyApp.xcworkspace",
      scheme: "MyApp",
      export_method: "app-store",
      derived_data_path: "DerivedData",
      output_directory: "fastlane/builds"
    )

    # Upload to TestFlight
    upload_to_testflight(
      skip_waiting_for_build_processing: true,
      api_key_path: "fastlane/AuthKey.json"
    )

    # Commit the version bump
    commit_version_bump(
      message: "chore: bump build number [skip ci]",
      force: true
    )

    # Tag the release
    add_git_tag(
      tag: "beta/#{lane_context[SharedValues::BUILD_NUMBER]}"
    )

    push_to_git_remote
  end

  # ---- RELEASE ----

  desc "Build and submit to App Store Review"
  lane :release do
    match(type: "appstore", readonly: true)

    # Increment version number (patch)
    increment_version_number(bump_type: "patch")
    increment_build_number(
      build_number: latest_testflight_build_number + 1
    )

    build_app(
      workspace: "MyApp.xcworkspace",
      scheme: "MyApp",
      export_method: "app-store",
      derived_data_path: "DerivedData"
    )

    upload_to_app_store(
      submit_for_review: true,
      automatic_release: false,
      api_key_path: "fastlane/AuthKey.json",
      precheck_include_in_app_purchases: false
    )

    commit_version_bump(message: "chore: release #{lane_context[SharedValues::VERSION_NUMBER]}")
    add_git_tag
    push_to_git_remote
  end

  # ---- ERROR HANDLING ----

  error do |lane, exception|
    # Send notification on failure (Slack, email, etc.)
    # slack(
    #   message: "Lane #{lane} failed: #{exception.message}",
    #   success: false
    # )
  end
end

5. Configura gli screenshot automatici

Fastlane Snapshot acquisisce automaticamente gli screenshot per l'App Store su più dispositivi e lingue. Su un Mac dedicato, funziona in modo affidabile senza competere per le risorse.

# Initialize snapshot
fastlane snapshot init

# This creates:
# - fastlane/Snapfile
# - fastlane/SnapshotHelper.swift (add to UI test target)

Configura lo Snapfile

# fastlane/Snapfile

devices([
  "iPhone 16 Pro Max",
  "iPhone 16 Pro",
  "iPhone SE (3rd generation)",
  "iPad Pro 13-inch (M4)"
])

languages([
  "en-US",
  "fr-FR",
  "de-DE",
  "ja"
])

scheme("MyAppUITests")
output_directory("./fastlane/screenshots")
clear_previous_screenshots(true)

# Speed up by running in parallel
concurrent_simulators(true)

Aggiungi Snapshot ai tuoi test UI

// In your XCUITest file:
import XCTest

class ScreenshotTests: XCTestCase {

    override func setUp() {
        continueAfterFailure = false
        let app = XCUIApplication()
        setupSnapshot(app)
        app.launch()
    }

    func testHomeScreen() {
        snapshot("01_HomeScreen")
    }

    func testDetailScreen() {
        let app = XCUIApplication()
        app.cells.firstMatch.tap()
        snapshot("02_DetailScreen")
    }

    func testSettings() {
        let app = XCUIApplication()
        app.tabBars.buttons["Settings"].tap()
        snapshot("03_Settings")
    }
}

Aggiungi una lane per gli screenshot

  # Add to your Fastfile:
  desc "Capture App Store screenshots"
  lane :screenshots do
    capture_screenshots
    frame_screenshots(white: true)  # Add device frames
    upload_to_app_store(
      skip_binary_upload: true,
      skip_metadata: true,
      api_key_path: "fastlane/AuthKey.json"
    )
  end

6. Distribuisci su TestFlight

Per gli ambienti CI, usa la chiave API di App Store Connect anziché le credenziali del tuo Apple ID. Questo evita le richieste 2FA sui server senza interfaccia grafica.

Crea una chiave API

  1. Vai su App Store Connect > Users and Access > Keys
  2. Clicca il pulsante + per generare una nuova chiave API
  3. Seleziona il ruolo App Manager
  4. Scarica il file .p8
  5. Annota il Key ID e l'Issuer ID

Crea il JSON della chiave API

# fastlane/AuthKey.json
{
  "key_id": "YOUR_KEY_ID",
  "issuer_id": "YOUR_ISSUER_ID",
  "key": "-----BEGIN PRIVATE KEY-----\nYOUR_P8_KEY_CONTENT\n-----END PRIVATE KEY-----",
  "in_house": false
}

# IMPORTANT: Add this to .gitignore!
echo "fastlane/AuthKey.json" >> .gitignore

Esegui il deployment

# Deploy to TestFlight
fastlane beta

# Or run the full release pipeline
fastlane release

7. Integra con la CI/CD

Fastlane si integra perfettamente con qualsiasi piattaforma CI/CD. Ecco un esempio con GitHub Actions usando il tuo runner Mac Mini M4 self-hosted:

# .github/workflows/deploy.yml
name: Deploy to TestFlight

on:
  push:
    branches: [ main ]

jobs:
  deploy:
    runs-on: [self-hosted, macOS, ARM64, M4]

    env:
      MATCH_PASSWORD: ${{ secrets.MATCH_PASSWORD }}
      MATCH_GIT_BASIC_AUTHORIZATION: ${{ secrets.MATCH_GIT_TOKEN }}

    steps:
      - name: Checkout
        uses: actions/checkout@v4

      - name: Set up App Store Connect API Key
        run: |
          mkdir -p fastlane
          echo '${{ secrets.APP_STORE_CONNECT_API_KEY }}' > fastlane/AuthKey.json

      - name: Install dependencies
        run: |
          bundle install
          pod install  # if using CocoaPods

      - name: Deploy to TestFlight
        run: bundle exec fastlane beta

      - name: Clean up API key
        if: always()
        run: rm -f fastlane/AuthKey.json

8. Best practice

Usa Bundler per fissare la versione di Fastlane

Aggiungi un Gemfile con una versione di Fastlane fissata. Esegui bundle exec fastlane per garantire versioni coerenti in tutti gli ambienti.

Usa le chiavi API di App Store Connect, non le credenziali Apple ID

Le chiavi API evitano le richieste 2FA e sono più sicure per gli ambienti CI.

Usa match --readonly in CI

Previene la creazione accidentale di nuovi certificati. Genera i certificati manualmente solo quando necessario.

Usa setup_ci negli ambienti CI

Questo crea un keychain temporaneo per evitare di alterare il keychain di sistema e previene le finestre di dialogo dei permessi del keychain.

Mantieni i DerivedData per build più veloci

Su un Mac dedicato, specifica derived_data_path per riutilizzare gli artefatti di build tra le esecuzioni.

Guide correlate

Automatizza i tuoi rilasci iOS oggi stesso

Ottieni un Mac Mini M4 dedicato ed esegui Fastlane con build illimitate. A partire da $85/mese.

Cerchi maggiori dettagli?

Consulta la documentazione completa per guide passo passo, riferimenti di configurazione e risoluzione dei problemi.

Apri la documentazione →