Nach einem Versionsupdate speicherte eine Nachrichten-App mehrere Hundert Megabyte erneut herunterladbarer Offline-Ressourcen unter Application Support. Alle Funktionstests waren erfolgreich und es gingen keine Benutzerdaten verloren, doch das Sicherungsvolumen stieg plötzlich stark an. Das Problem lag nicht in der Downloadlogik, sondern darin, dass das Team nie in ausführbaren Regeln festgelegt hatte, welche Dateien wiederhergestellt werden müssen und welche sich neu erzeugen lassen. Ein Cloud-Mac eignet sich gut für solche Regressionstests: Die Umgebung bleibt langfristig konsistent, Simulatoren lassen sich zurücksetzen und bei Fehlern kann eine Liste des Containers für die Analyse aufbewahrt werden.
Zuerst die Wiederherstellungssemantik der Daten definieren
Tests sollten nicht zuerst nach Verzeichnissen, sondern nach dem Verwendungszweck der Daten strukturiert werden. Dieselbe JSON-Datei kann vom Benutzer erstellte Inhalte enthalten oder lediglich ein API-Cache sein. Daraus ergeben sich völlig unterschiedliche Anforderungen an die Sicherung.
| Datentyp | Empfohlener Speicherort | Erwartung an die Sicherung | Beispiele |
|---|---|---|---|
| Vom Benutzer erstellt und nicht rekonstruierbar | Documents |
Muss erhalten bleiben | Entwürfe, vom Benutzer importierte Dateien |
| Persistenter Anwendungsstatus | Application Support |
Abhängig von den fachlichen Anforderungen | Datenbanken, Bearbeitungsfortschritt |
| Erneut generier- oder herunterladbar | Library/Caches |
Darf nicht von einer Sicherung abhängen | Bild-Cache, Offline-Index |
| Temporäre Dateien eines einzelnen Vorgangs | tmp |
Nicht aufbewahren | Entpackverzeichnisse, Dateifragmente |
Application Support wird besonders häufig falsch verwendet. Das Verzeichnis ist für Daten geeignet, die langfristig von der Anwendung verwaltet werden. Das bedeutet jedoch nicht, dass sämtliche Inhalte in eine Sicherung aufgenommen werden sollten. Große Modelle, Kartenpakete oder Proxy-Dateien für Medien sollten ausdrücklich von Sicherungen ausgeschlossen oder unter Caches abgelegt werden, wenn sie erneut bezogen werden können.
Entscheidend ist nicht, ob eine Datei wichtig ist, sondern ob sie nach der Wiederherstellung eines Geräts zwingend aus der Sicherung zurückkehren muss und nicht aus einer zuverlässigen Quelle rekonstruiert werden kann.
Fassen Sie die Regeln in einer Liste im Repository zusammen. Darin können beispielsweise der logische Name, der relative Pfad, die Wiederherstellungsanforderung und die verantwortliche Person festgehalten werden. Tests sollen diese Vereinbarung lediglich durchsetzen. Zufällige, über den Code verteilte Pfade dürfen nicht zur Spezifikation werden.
Ausschlussattribute mit XCTest absichern
Foundation bietet eine stabilere Schnittstelle als das direkte Auslesen systemnaher erweiterter Attribute. Die folgende Hilfsmethode setzt nach dem Erstellen einer Datei isExcludedFromBackup und liest anschließend den Ressourcenwert erneut ein, um zu prüfen, ob die Einstellung dauerhaft gespeichert wurde.
import XCTest
final class BackupBoundaryTests: XCTestCase {
private func markExcluded(_ url: URL) throws {
var values = URLResourceValues()
values.isExcludedFromBackup = true
var mutableURL = url
try mutableURL.setResourceValues(values)
}
func testDownloadPackageIsExcludedFromBackup() throws {
let root = FileManager.default.urls(
for: .applicationSupportDirectory,
in: .userDomainMask
)[0]
let package = root.appendingPathComponent(
"Downloads/catalog.bundle",
isDirectory: false
)
try FileManager.default.createDirectory(
at: package.deletingLastPathComponent(),
withIntermediateDirectories: true
)
try Data("fixture".utf8).write(to: package)
try markExcluded(package)
let values = try package.resourceValues(
forKeys: [.isExcludedFromBackupKey]
)
XCTAssertEqual(values.isExcludedFromBackup, true)
}
}
Der Test soll nicht beweisen, dass Foundation funktioniert. Er soll sicherstellen, dass auch der Produktionscode nach dem Erstellen einer Datei denselben Pfad zum Setzen des Attributs durchläuft. Robuster ist es, Verzeichniserstellung, atomisches Schreiben und Ausschlussmarkierung in einer Speicherkomponente zu kapseln und diese Komponente direkt im Test aufzurufen. Andernfalls kann der Test das Attribut erfolgreich setzen, während der produktive Downloader diesen Schritt auslässt und die CI-Schranke wirkungslos bleibt.
Auch falsch abgelegte Dateien prüfen
Ergänzen Sie negative Assertions: Benutzerentwürfe dürfen nicht unter Caches landen, und temporäre Exportdateien dürfen nicht in Documents verbleiben. Die Test-Fixtures sollten Erstellung, Upgrade-Migrationen und fehlgeschlagene Wiederholungsversuche abdecken. Pfade driften häufig in Migrationszweigen ab und nicht bei einer Erstinstallation.
Bei Ausschlüssen auf Verzeichnisebene sollten außerdem neu erstellte untergeordnete Dateien stichprobenartig geprüft werden. Gehen Sie nicht davon aus, dass das aktuelle Attribut des übergeordneten Verzeichnisses die Schreiblogik dauerhaft ersetzen kann. Wenn die Komponente das Attribut nach jeder Verzeichniserstellung aktiv bestätigt, ist sie widerstandsfähiger gegenüber Migrationen alter Daten und neu angelegten Verzeichnissen.
Container-Belege im Simulator aufbewahren
Unit-Tests eignen sich zum Prüfen der Regeln. Die Untersuchung des Simulator-Containers beantwortet dagegen die Frage, was bei einem Fehler tatsächlich geschrieben wurde. Lassen Sie die Tests zunächst ein festgelegtes Gerät und ein separates DerivedData-Verzeichnis verwenden, damit mehrere Jobs keinen gemeinsamen Zustand nutzen.
set -euo pipefail
DERIVED_DATA="$PWD/.ci/DerivedData-backup"
RESULT_BUNDLE="$PWD/.ci/BackupBoundary.xcresult"
rm -rf "$DERIVED_DATA" "$RESULT_BUNDLE"
xcodebuild test \
-workspace Example.xcworkspace \
-scheme Example \
-destination 'platform=iOS Simulator,name=iPhone 16' \
-derivedDataPath "$DERIVED_DATA" \
-resultBundlePath "$RESULT_BUNDLE"
APP_DATA="$(
xcrun simctl get_app_container booted com.example.app data
)"
find "$APP_DATA" -type f -print | LC_ALL=C sort \
> "$PWD/.ci/app-container-files.txt"
get_app_container ist nur erfolgreich, wenn die Ziel-App installiert und der zugehörige Simulator gestartet ist. Die CI darf nicht stillschweigend irgendein bereits gestartetes Gerät auswählen. Sie sollte zu Beginn des Jobs ein Gerät erstellen oder explizit festlegen, den Abschluss des Startvorgangs abwarten und das Gerät am Ende wieder herunterfahren. Werden auf Runner mehrere Testsuiten parallel ausgeführt, sollte jeder Job einen eigenen Simulator-Gerätesatz verwenden, damit Container nicht verwechselt werden.
Sicherer und leichter vergleichbar ist eine Dateiliste, die ausschließlich relative Pfade enthält. Home-Verzeichnisse, absolute Workspace-Pfade und Testzugangsdaten gehören nicht in die Artefakte. Wenn erweiterte Attribute untersucht werden müssen, kann die Ausgabe von xattr als Diagnoseanhang gespeichert werden. Die Entscheidung der CI-Schranke sollte sich dennoch auf die Ressourcenwerte von Foundation und die fachliche Liste stützen, damit sie nicht von nicht zugesichertem systemnahem Verhalten abhängt.
Auffällige Größenänderungen in nachvollziehbare Fehler umwandeln
Boolesche Attribute allein zu prüfen, genügt nicht. Nach einer Pfadrefaktorierung könnten sämtliche Cache-Dateien unter Documents landen, obwohl für keine einzelne Datei ein Test des Ausschlussattributs fehlschlägt. Deshalb können für die obersten Verzeichnisse des Containers Größenbudgets definiert werden. Diese Budgets sollten strukturelle Anomalien ausdrücken und keine exakten Bytezahlen erzwingen.
Nach dem Durchlauf der Test-Fixtures kann beispielsweise verlangt werden, dass Documents nur die festgelegten Beispieldateien enthält, tmp am Ende des Vorgangs leer ist und Caches zwar wachsen darf, aber keine Dateiendungen von Benutzerentwürfen enthält. Verwenden Sie für große Testressourcen eine ausdrückliche Positivliste. Fehlermeldungen sollten den relativen Pfad, die Dateigröße, die zugehörige Regel und die Erstellungsphase ausgeben.
Den Fehlerzustand nicht sofort löschen
Die erste Reaktion auf einen Fehler sollte nicht darin bestehen, den Simulator zu löschen. Archivieren Sie zunächst folgende Informationen:
- das XCTest-Ergebnispaket und den Namen des fehlgeschlagenen Testfalls;
- eine Liste der relativen Pfade im Anwendungscontainer;
- die Gesamtgröße der einzelnen Verzeichnisse auf oberster Ebene;
- den getesteten Commit, das Scheme und die Simulator-Laufzeit;
- den Testschritt, der die Dateierstellung ausgelöst hat.
Diese Belege reichen aus, um zwischen einem nicht gesetzten Attribut, einer Datei im falschen Verzeichnis und einem nicht ausgeführten Bereinigungsvorgang zu unterscheiden. Löschen Sie das Gerät erst, nachdem alle Anhänge gesichert wurden. Andernfalls bleibt von einem sporadischen Fehler möglicherweise nur eine einzelne fehlgeschlagene Assertion übrig.
Eine stabile CI-Schranke entwerfen
Tests der Sicherungsgrenzen sollten bei jeder Änderung an der Speicherschicht, am Downloader, an Datenbankmigrationen oder am Exportablauf ausgeführt werden. Sie können außerdem Teil einer schlanken Testsuite vor dem Zusammenführen sein. Koppeln Sie sie nicht an Ende-zu-Ende-Abläufe, die externe Dienste benötigen. Nur lokale Fixtures und Daten mit festgelegter Größe liefern reproduzierbare Ergebnisse.
Es empfiehlt sich, Fehler in drei Kategorien einzuteilen: falsch platzierte Benutzerdaten, die den Prozess blockieren müssen; fehlende Ausschlussattribute, die ebenfalls blockieren müssen; und Trends bei der Speichernutzung, die lediglich eine Warnung auslösen. Schwellenwerte für die Größe müssen über eine Konfiguration im Repository gesteuert werden. Änderungen daran sind im Review zu begründen und dürfen nicht automatisch vom Skript gelockert werden, sobald ein Grenzwert überschritten wird.
Abschließend sollte eine Abnahme auf einem realen Gerät erhalten bleiben. Der Simulator kann Verzeichnisse, Attribute und Migrationscode zuverlässig prüfen, deckt aber nicht das gesamte Verhalten einer echten Wiederherstellung ab. Für die Release-Prüfung sollte ein minimaler Datensatz verwendet werden, um zu bestätigen, dass nicht rekonstruierbare Daten wiederhergestellt werden können und reproduzierbare Daten nicht zur Voraussetzung für eine Wiederherstellung werden. So übernimmt die automatisierte CI-Schranke die häufigen Regressionstests, während der Ablauf auf dem realen Gerät die endgültigen Grenzen bestätigt. Keines der beiden Verfahren ersetzt das andere.
Häufig gestellte Fragen
Welche iOS-Dateien sollten üblicherweise nicht gesichert werden?
Erneut ladbare Caches, Offline-Pakete, entpackte Zwischenstände und temporäre Exporte gehören meist nach Caches oder tmp beziehungsweise benötigen das Attribut isExcludedFromBackup.
Warum reicht eine Prüfung des Verzeichnisses nicht aus?
Migrationen und Bibliotheken können Dateien falsch ablegen. Deshalb muss der Test zusätzlich Verwendungszweck, Ausschlussattribut und das erwartete Verhalten nach einer Neuinstallation prüfen.
Ersetzt der Simulator eine Prüfung auf einem realen Gerät?
Nein. Der Simulator eignet sich für wiederholbare Verzeichnis- und Attributprüfungen, während Sicherung, Wiederherstellung und Datenschutz vor einer Veröffentlichung kontrolliert auf einem realen Gerät geprüft werden sollten.
Den nächsten Auftrag auf einem Cloud-M4-Mac ausführen
Wählen Sie Runner M4 oder Runner M4 Plus und legen Sie Knoten, Laufzeit und zusätzliche Speicheroptionen passend zu Ihrem Projekt fest. Jede Bestellung umfasst einen dedizierten physischen Rechner, keine virtuelle Maschine.