1. Warum Fastlane auf einem dedizierten Mac?
Fastlane ist das Standard-Automatisierungstool für die iOS- und Android-Entwicklung. Es kümmert sich um alles von der Code-Signierung bis zum TestFlight-Deployment. Fastlane auf einem dedizierten Mac Mini M4 auszuführen bietet Ihnen:
Code-Signing-Zertifikate bleiben zwischen den Durchläufen erhalten. Kein Import/Export bei jedem Build erforderlich.
DerivedData bleibt erhalten, sodass fastlane build 2-4 Minuten statt über 15 dauert.
Führen Sie fastlane snapshot mit echten Simulatoren für alle Gerätegrößen aus.
Eine stabile, dedizierte Umgebung bedeutet weniger instabile Deployments und Code-Signing-Probleme.
2. Fastlane installieren
Verbinden Sie sich per SSH mit Ihrem Mac Mini M4 und installieren Sie Fastlane. Wir empfehlen Homebrew für die einfachste Einrichtung:
Option A: Installation über Homebrew (empfohlen)
# 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
Option B: Installation über 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
Fastlane in Ihrem Projekt initialisieren
# 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. Match für die Code-Signierung konfigurieren
Fastlane Match speichert Ihre Code-Signing-Zertifikate und Provisioning-Profile in einem privaten Git-Repository oder Cloud-Speicher. Dadurch wird sichergestellt, dass alle Maschinen (und Teammitglieder) dieselbe Signaturidentität verwenden.
Match initialisieren
# Initialize match (choose "git" for storage)
fastlane match init
# This creates fastlane/Matchfile
Matchfile konfigurieren
# 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")
Zertifikate generieren
# 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. Fastfile erstellen (Build-, Test-, Deploy-Lanes)
Hier ist ein vollständiges Fastfile mit Lanes zum Bauen, Testen und Deployen Ihrer iOS-App:
# 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. Automatisierte Screenshots einrichten
Fastlane Snapshot erstellt automatisch App-Store-Screenshots über mehrere Geräte und Sprachen hinweg. Auf einem dedizierten Mac läuft dies zuverlässig, ohne um Ressourcen zu konkurrieren.
# Initialize snapshot
fastlane snapshot init
# This creates:
# - fastlane/Snapfile
# - fastlane/SnapshotHelper.swift (add to UI test target)
Snapfile konfigurieren
# 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)
Snapshot zu Ihren UI-Tests hinzufügen
// 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")
}
}
Eine Screenshot-Lane hinzufügen
# 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. Auf TestFlight deployen
Verwenden Sie für CI-Umgebungen den App Store Connect API-Schlüssel anstelle Ihrer Apple-ID-Anmeldedaten. Dies vermeidet 2FA-Aufforderungen auf Servern ohne Bildschirm.
Einen API-Schlüssel erstellen
- Gehen Sie zu App Store Connect > Users and Access > Keys
- Klicken Sie auf die Schaltfläche +, um einen neuen API-Schlüssel zu generieren
- Wählen Sie die Rolle App Manager
- Laden Sie die
.p8-Datei herunter - Notieren Sie sich die Key ID und die Issuer ID
Das API-Schlüssel-JSON erstellen
# 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
Das Deployment ausführen
# Deploy to TestFlight
fastlane beta
# Or run the full release pipeline
fastlane release
7. In CI/CD integrieren
Fastlane lässt sich nahtlos in jede CI/CD-Plattform integrieren. Hier ist ein GitHub-Actions-Beispiel mit Ihrem selbst gehosteten Mac Mini M4 Runner:
# .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 Practices
Fügen Sie ein Gemfile mit einer festgelegten Fastlane-Version hinzu. Führen Sie bundle exec fastlane aus, um konsistente Versionen in allen Umgebungen sicherzustellen.
API-Schlüssel vermeiden 2FA-Aufforderungen und sind für CI-Umgebungen sicherer.
match --readonly verwenden
Verhindert die versehentliche Erstellung neuer Zertifikate. Generieren Sie Zertifikate nur bei Bedarf manuell.
setup_ci verwenden
Dies erstellt einen temporären Schlüsselbund, um den System-Schlüsselbund nicht zu belasten, und verhindert Berechtigungsdialoge für den Schlüsselbund.
Geben Sie auf einem dedizierten Mac derived_data_path an, um Build-Artefakte zwischen den Durchläufen wiederzuverwenden.
Verwandte Anleitungen
GitHub Actions selbst gehosteter Runner
Richten Sie einen GitHub Actions Runner auf Ihrem Mac Mini M4 ein.
Jenkins Mac Build-Agent
Konfigurieren Sie Jenkins Build-Agents auf einem dedizierten Mac Mini für iOS CI/CD.
GitLab CI Mac Runner
Registrieren Sie einen GitLab Runner auf einem dedizierten Mac Mini für iOS/macOS CI-Pipelines.