Wenn dasselbe Xcode-Archiv den Distributionsprozess durchläuft, sind die Inhalte, die letztlich auf verschiedenen Geräten installiert werden, nicht vollständig identisch. App Thinning wählt anhand der Gerätefähigkeiten die passenden ausführbaren Slices und Ressourcenvarianten aus. Bei alltäglichen Builds wird häufig nur geprüft, ob die Archivierung erfolgreich war. Dadurch bleiben fehlerhafte Ressourcen-Tags, überflüssige Architekturen, Abweichungen bei eingebetteten Frameworks oder ein vollständig fehlender Größenbericht leicht unentdeckt. Zuverlässiger ist es, „Archivierung“ und „Abnahme der Gerätevarianten“ auf einem Cloud Mac in zwei separate Phasen aufzuteilen.
Dieser Artikel verwendet ein bestehendes Projekt und eine distributionsfähige Archivkonfiguration. Das Signierungssystem bleibt unverändert, und eine erneute Kompilierung bei jedem Prüflauf ist nicht erforderlich. Ziel ist es, aus einer einzelnen .xcarchive die App-Thinning-Ergebnisse zu exportieren, Strukturübersichten zu erstellen und CI bei klar definierten Abweichungen mit einem Exitcode ungleich null fehlschlagen zu lassen.
Abnahmeeingaben zuerst festlegen
Vor Beginn der Abnahme sollten der Xcode-Pfad, die App-Version, die Buildnummer und der Speicherort des Archivs protokolliert werden. Der Job darf nicht von der Standard-Toolchain einer interaktiven Shell abhängen. Auf einem Cloud Mac können mehrere Xcode-Versionen parallel installiert sein. CI sollte daher ausdrücklich die für die aktuelle Pipeline freigegebene Version auswählen.
set -euo pipefail
export DEVELOPER_DIR="/Applications/Xcode.app/Contents/Developer"
ARCHIVE_PATH="$PWD/output/App.xcarchive"
EXPORT_DIR="$PWD/output/thinning"
REPORT_DIR="$PWD/output/evidence"
xcodebuild -version
test -d "$ARCHIVE_PATH"
rm -rf "$EXPORT_DIR" "$REPORT_DIR"
mkdir -p "$EXPORT_DIR" "$REPORT_DIR"
Anschließend werden die App-Informationen aus dem Archiv ausgelesen, um sicherzustellen, dass CI nicht versehentlich ein Artefakt aus einem vorherigen Job verwendet.
INFO="$ARCHIVE_PATH/Info.plist"
/usr/libexec/PlistBuddy -c "Print :ApplicationProperties:CFBundleIdentifier" "$INFO"
/usr/libexec/PlistBuddy -c "Print :ApplicationProperties:CFBundleShortVersionString" "$INFO"
/usr/libexec/PlistBuddy -c "Print :ApplicationProperties:CFBundleVersion" "$INFO"
Es empfiehlt sich, das Arbeitsverzeichnis an die Job-ID zu binden und zu Beginn jedes Jobs ein leeres Verzeichnis anzulegen. Mehrere parallele Jobs sollten sich kein globales, fest definiertes output-Verzeichnis für ihre Archive teilen.
Die Eingabe für die App-Thinning-Abnahme muss eine einzelne
.xcarchivesein, die bereits die regulären Archivprüfungen bestanden hat. Wenn schon die Eingabe nicht eindeutig feststeht, sind selbst detaillierte Vergleiche der Gerätevarianten nicht aussagekräftig.
Alle kompatiblen Gerätevarianten exportieren
Erstellen Sie eine separate ExportOptions.plist. Der Wert für method muss zu der vom Team eingesetzten Xcode-Version und zum bestehenden Distributionsprozess passen. Entscheidend ist im folgenden Beispiel, dass thinning für den Export aller kompatiblen Varianten konfiguriert wird.
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "https://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>method</key>
<string>app-store-connect</string>
<key>thinning</key>
<string><thin-for-all-variants></string>
<key>stripSwiftSymbols</key>
<true/>
</dict>
</plist>
Beim Export werden sowohl die Ergebnisse als auch die Protokolle im Verzeichnis des aktuellen Jobs gespeichert:
xcodebuild \
-exportArchive \
-archivePath "$ARCHIVE_PATH" \
-exportPath "$EXPORT_DIR" \
-exportOptionsPlist "$PWD/ExportOptions.plist" \
2>&1 | tee "$REPORT_DIR/export.log"
find "$EXPORT_DIR" -maxdepth 3 -type f -print \
| LC_ALL=C sort \
> "$REPORT_DIR/export-files.txt"
Falls die bestehende Pipeline eine andere Exportmethode verwendet, sollte diese nicht nur zur Übernahme des Beispiels geändert werden. Behalten Sie den vorhandenen Wert für method bei, validieren Sie lediglich die thinning-Konfiguration in einem isolierten Branch und archivieren Sie die tatsächlich verwendete plist-Datei zusammen mit den Ergebnissen.
Architekturen und eingebettete Inhalte prüfen
Ein erfolgreicher Export bedeutet noch nicht, dass die Ergebnisse korrekt sind. Zunächst werden alle Installationspakete gesucht und einzeln entpackt. Danach wird die Architektur der ausführbaren Hauptdatei geprüft. Das Skript sollte keinen festen Namen für das App-Verzeichnis voraussetzen, sondern die .app innerhalb von Payload ermitteln.
mkdir -p "$REPORT_DIR/unpacked"
while IFS= read -r ipa; do
name="$(basename "$ipa" .ipa)"
dest="$REPORT_DIR/unpacked/$name"
mkdir -p "$dest"
ditto -x -k "$ipa" "$dest"
app="$(find "$dest/Payload" -maxdepth 1 -name '*.app' -type d | head -n 1)"
test -n "$app"
executable="$(/usr/libexec/PlistBuddy \
-c 'Print :CFBundleExecutable' "$app/Info.plist")"
printf '%s: ' "$name"
lipo -archs "$app/$executable"
find "$app/Frameworks" -maxdepth 1 -type d -name '*.framework' 2>/dev/null \
| sed "s#^$app/##" \
| LC_ALL=C sort \
> "$REPORT_DIR/$name-frameworks.txt"
done < <(find "$EXPORT_DIR" -type f -name '*.ipa' | LC_ALL=C sort)
Bei aktuellen Distributionsergebnissen für iOS-Geräte muss der Job unmittelbar fehlschlagen, wenn die Haupt-App eine Simulatorarchitektur enthält. Die Framework-Liste eignet sich für einen Vergleich mit einer im Repository hinterlegten, freigegebenen Baseline. Ein neues Framework ist nicht zwangsläufig ein Fehler, muss aber einer Codeprüfung unterzogen werden, damit keine Debug-Komponenten oder temporären Abhängigkeiten in das Auslieferungspaket gelangen.
Nicht den Hash des gesamten Installationspakets vergleichen
Verschiedene Gerätevarianten dürfen unterschiedliche Ressourcen und ausführbare Slices enthalten. Auch Signaturdaten führen zu binären Unterschieden. Wenn für alle IPA-Dateien derselbe SHA-256-Wert verlangt wird, wird reguläres App Thinning fälschlicherweise als Fehler eingestuft.
Sinnvollere Vergleichsobjekte sind:
- die zulässigen Architekturen der ausführbaren Hauptdatei;
- Listen der relativen Pfade unter
FrameworksundPlugIns; - erforderliche Lokalisierungen, Schriftarten und Datendateien;
- Debug-Konfigurationen, Testressourcen und Quelldateien, die nicht in das fertige Produkt gelangen dürfen;
- die entpackte Größe jeder Variante und ihre Abweichung von der historischen Baseline.
Ressourcen und Größen als Nachweise erfassen
Prüfen Sie zunächst, ob der Exportprozess tatsächlich einen Größenbericht erzeugt hat. Erstellen Sie anschließend für jede Variante eine Dateiliste. Da sich Dateigrößen durch die Ressourcenoptimierung unterscheiden können, sollte die Baseline für jede Variante separat gespeichert werden.
SIZE_REPORT="$(find "$EXPORT_DIR" -type f \
-name 'App Thinning Size Report.txt' | head -n 1)"
test -n "$SIZE_REPORT"
cp "$SIZE_REPORT" "$REPORT_DIR/"
for app in "$REPORT_DIR"/unpacked/*/Payload/*.app; do
variant="$(basename "$(dirname "$(dirname "$app")")")"
(
cd "$app"
find . -type f -print0 \
| xargs -0 stat -f '%z %N' \
| LC_ALL=C sort
) > "$REPORT_DIR/$variant-files-and-sizes.txt"
done
Für die Größenprüfung sollte nicht nur ein einzelner, unveränderlicher Absolutwert festgelegt werden. Empfehlenswert sind zwei parallele Kriterien: eine vom Team freigegebene feste Obergrenze und die zulässige Zunahme gegenüber der letzten stabilen Baseline. Die feste Obergrenze verhindert offensichtlich außer Kontrolle geratene Größen, während der relative Vergleich ungewöhnliche Zuwächse durch einen einzelnen Commit erkennt.
Bei Ressourcenverzeichnissen sollte zusätzlich geprüft werden, ob geschäftskritische Dateien vorhanden sind. Offline-Vorlagen, Schriftarten für den ersten Bildschirm oder bestimmte Sprachressourcen können nicht allein dadurch als vollständig gelten, dass die App startet. Pflegen Sie diese Pfade als Textliste im Repository und prüfen Sie jeden Eintrag mit test -e. Das lässt sich leichter überprüfen als eine große Zahl über das Skript verteilter Bedingungen.
Eindeutige Fehlerbedingungen in CI definieren
Das abschließende Skript sollte mindestens folgende Fälle blockieren: Es wurde keine IPA exportiert, der Größenbericht fehlt, die Haupt-App enthält eine nicht zulässige Architektur, kritische Ressourcen fehlen, die Framework-Menge hat sich ohne Freigabe geändert oder die Größe einer Variante überschreitet das vom Team festgelegte Limit.
Folgende Nachweise sollten ebenfalls gespeichert werden:
- Xcode-Version und
DEVELOPER_DIR; - Bundle-ID, Version und Buildnummer des Archivs;
- die tatsächlich verwendete
ExportOptions.plist; - das vollständige Exportprotokoll;
- Architektur-, Framework- und Dateilisten für jede Variante;
- der App-Thinning-Größenbericht.
Bei einem Fehler darf das Exportverzeichnis nicht sofort gelöscht werden. Packen Sie zunächst die Nachweise und laden Sie sie hoch. Erst danach sollte ein Exit-Hook des Jobs den Arbeitsbereich bereinigen. Bei paralleler Ausführung auf Cloud Macs von PingMini muss jeder Job ein eigenes Verzeichnis verwenden. Außerdem sollte in der Konsole geprüft werden, welche Konfigurationen aktuell verfügbar sind. Die Abnahmelogik selbst darf nicht von einem bestimmten Knoten abhängen.
Der Nutzen von App Thinning besteht nicht nur in einer geringeren Downloadgröße. App Thinning verändert das tatsächlich ausgelieferte Endprodukt. Nur wenn Gerätevarianten wie reguläre Build-Artefakte geprüft werden, lassen sich Änderungen an Ressourcen-Tags, Architekturen oder Abhängigkeiten bereits vor der Veröffentlichung als reproduzierbare und klar lokalisierbare Fehler erkennen.
Häufig gestellte Fragen
Soll die Prüfung am Xcode-Archiv oder an den exportierten Paketen erfolgen?
An beiden. Das Archiv bestätigt die korrekte Eingabe, während erst die exportierten Varianten zeigen, welche Architekturen und Ressourcen tatsächlich pro Geräteklasse ausgeliefert werden.
Müssen die Hashes aller App-Thinning-Varianten identisch sein?
Nein. Binärsegmente, Ressourcen und Signaturdaten dürfen abweichen. Verglichen werden sollten erlaubte Architekturen, Dateilisten, wichtige Ressourcen und unerwartete neue Komponenten.
Welche Fehler sollten den CI-Lauf sofort stoppen?
Ein fehlender Größenbericht, ein leerer Export, unerlaubte Architekturen, fehlende Pflichtressourcen, abweichende Framework-Listen oder eine überschrittene Größenobergrenze sollten den Lauf stoppen.
Exklusiven physischen Mac für den nächsten Build vorbereiten
Wählen Sie aus drei festen Konfigurationen den passenden Cloud Mac für Entwicklung, Continuous Integration oder lokale Modelltests und richten Sie ihn passend zur Dauer Ihres Projekts ein.