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:
I certificati di code signing persistono tra le esecuzioni. Nessun bisogno di importarli/esportarli a ogni build.
I DerivedData persistono, quindi fastlane build richiede 2-4 minuti invece di oltre 15.
Esegui fastlane snapshot con simulatori reali per tutte le dimensioni dei dispositivi.
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
- Vai su App Store Connect > Users and Access > Keys
- Clicca il pulsante + per generare una nuova chiave API
- Seleziona il ruolo App Manager
- Scarica il file
.p8 - 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
Aggiungi un Gemfile con una versione di Fastlane fissata. Esegui bundle exec fastlane per garantire versioni coerenti in tutti gli ambienti.
Le chiavi API evitano le richieste 2FA e sono più sicure per gli ambienti CI.
match --readonly in CI
Previene la creazione accidentale di nuovi certificati. Genera i certificati manualmente solo quando necessario.
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.
Su un Mac dedicato, specifica derived_data_path per riutilizzare gli artefatti di build tra le esecuzioni.
Guide correlate
Runner self-hosted GitHub Actions
Configura un runner GitHub Actions sul tuo Mac Mini M4.
Agent di build Jenkins per Mac
Configura gli agent di build Jenkins su un Mac Mini dedicato per la CI/CD iOS.
Runner GitLab CI per Mac
Registra un GitLab Runner su un Mac Mini dedicato per le pipeline CI iOS/macOS.