Engineering-Praxis

WidgetKit-Zeitpläne und gemeinsame Daten auf einem Cloud-Mac prüfen

WidgetKit-Zeitpläne und gemeinsame Daten auf einem Cloud-Mac prüfen

Das Team hat die Anzahl offener Aufgaben in der Haupt-App gespeichert, doch das Widget auf dem entfernten Simulator zeigt weiterhin den alten Wert. Jetzt einfach öfter eine Aktualisierung anzufordern, erzeugt meist nur mehr Rauschen bei der Fehlersuche: Das Schreiben gemeinsamer Daten, ihr Lesen durch die Erweiterung, das Erstellen der Timeline und die Anzeige durch das System sind vier getrennte Schritte. An einem Widget für die Anzahl offener Aufgaben lässt sich jeder Schritt einzeln überprüfen.

Die Grenze der Datenübergabe klären

Haupt-App und Widget-Erweiterung laufen nicht im selben Prozess. Ein gewöhnliches UserDefaults.standard wird nicht automatisch zur gemeinsamen Datenquelle. Aktiviere zuerst dieselbe App Group für beide Targets im Projekt und hinterlege die Gruppenkennung in einer Konfiguration, die beide lesen können. Eine Kennung für Tests darf nicht fest in der Release-Konfiguration stehen.

Nachdem die Haupt-App die Fachdaten geschrieben hat, speichert sie Anzahl und Aktualisierungszeitpunkt in UserDefaults(suiteName:). Die Erweiterung liest sie unter demselben Suite-Namen. Die Schreibfunktion sollte einen Erfolg oder Fehler zurückgeben, statt bei einem Fehler stillschweigend den alten Wert weiterzuverwenden. Schlägt das Lesen in der Erweiterung fehl, zeigt sie einen eindeutig erkennbaren Standardzustand. Bestehen die Daten aus mehr als einer Ganzzahl, kodiere sie als einen Datensatz mit Versionsnummer. So lässt sich vermeiden, dass eine neue Anzahl mit einem alten Titel kombiniert wird.

Eine Aktualisierungsanforderung teilt dem System lediglich mit, dass sich Daten geändert haben könnten. Sie überträgt weder Daten von der Haupt-App in die Erweiterung noch garantiert sie, dass das Widget sofort neu gezeichnet wird.

Lies den Wert nach dem Schreiben zunächst in der Haupt-App zurück und protokolliere anschließend beim Lesen in der Erweiterung die Versionsnummer ohne sensible Daten. Unterscheiden sich die Versionen, liegt das Problem bei der Datenübergabe. Stimmen sie überein, aber die Anzeige weicht ab, prüfe erst dann Timeline und Darstellung. Aufgabeninhalte, Kontodaten oder vollständige gemeinsame Datensätze gehören nicht in Diagnoseprotokolle.

Die Timeline-Berechnung zur reinen Funktion machen

Der Inhalt der Timeline-Einträge sollte nicht von der tatsächlichen Uhrzeit während des Tests abhängen. Der Provider übernimmt nur das Lesen der Daten und den Aufruf einer Berechnungsfunktion. Diese erhält die aktuelle Zeit und den gelesenen Snapshot als Eingaben. Der folgende Code zeigt die kleinste sinnvolle Trennung; die Typen können in einer Quelldatei liegen, die Haupt-App und Erweiterung gemeinsam nutzen.

import Foundation

struct CountEntry: Equatable {
    let date: Date
    let count: Int
    let revision: Int
}

func makeEntries(
    now: Date,
    count: Int,
    revision: Int
) -> [CountEntry] {
    [
        CountEntry(date: now, count: count, revision: revision),
        CountEntry(
            date: now.addingTimeInterval(15 * 60),
            count: count,
            revision: revision
        )
    ]
}

Der zweite Eintrag enthält weiterhin denselben Snapshot. Aus dem Ablauf von fünfzehn Minuten lässt sich nicht ableiten, dass sich die Anzahl offener Aufgaben geändert hat. Nachdem der Provider die Einträge erzeugt hat, kann er für die Timeline eine erneute Abfrage nach dem letzten Eintrag vorsehen. Diese Strategie gibt einen gewünschten Prüfzeitpunkt an, keine Zusage für eine Aktualisierung der Oberfläche auf die Sekunde genau. Treffen neue Fachdaten ein, fordere das erneute Laden der betreffenden Widget-Art an. Soll das Produkt einen präzisen Countdown anzeigen, muss die Anforderung neu bewertet werden, statt einen Echtzeitprozess mit dicht aufeinanderfolgenden Timeline-Einträgen nachzubilden.

Feste Eingaben, überprüfbare Ergebnisse

Erzeuge im Unit-Test ein festes Date, eine feste Anzahl und eine feste Revisionsnummer. Prüfe, dass genau zwei Einträge entstehen, ihre Zeitpunkte streng aufsteigend sind und Anzahl sowie Revision in beiden Einträgen übereinstimmen. Teste außerdem den Grenzfall einer Anzahl von null: Null muss als tatsächlicher Wert dargestellt werden und darf nicht vom Standardwert für einen Lesefehler verdeckt werden. Diese Tests starten keinen Simulator und warten nicht auf die Systemplanung. Schlagen sie fehl, lässt sich der Fehler direkt der Berechnungslogik zuordnen.

Die tatsächliche Datenübergabe im Cloud-Simulator prüfen

Ermittle mit xcrun simctl list devices available einen verfügbaren iOS-Simulator und seine Geräte-ID. Verwende beim Bauen und Testen eine feste Ziel-ID. So führt ein Wechsel des Ausführungsrechners nicht dazu, dass über den Gerätenamen eine andere Runtime ausgewählt wird:

xcodebuild test \
  -project Widgets.xcodeproj \
  -scheme Widgets \
  -destination "platform=iOS Simulator,id=$SIM_ID" \
  -resultBundlePath "$PWD/widget-tests.xcresult"

Projektname und Scheme sind hier Beispiele; ersetze sie vor der Ausführung durch Namen, die im Repository tatsächlich existieren. SIM_ID sollte ausdrücklich über die Aufgabenkonfiguration übergeben werden. Verwende bei jedem Lauf ein neues Ergebnisverzeichnis, damit eine vorhandene .xcresult den Befehl nicht vorzeitig scheitern lässt. Wenn die Timeline-Unit-Tests bestanden sind, installiere einen Build mit Haupt-App und Erweiterung und füge das Widget im Simulator hinzu: Prüfe zuerst den Standardzustand, schreibe dann aus der Haupt-App eine neue Revision und fordere eine Aktualisierung an. Vergleiche zuletzt den von der Erweiterung gelesenen Datensatz mit der Anzeige.

Bei der Abnahme der Oberfläche zählen nur der endgültige Inhalt und die Revisionsnummer. „Erscheint innerhalb weniger Sekunden nach der Anforderung“ darf keine harte Testbedingung sein. Das System kann den Aktualisierungszeitpunkt verschieben. Muss die sofortige Anzeige exakt geprüft werden, teste eine kontrollierbare Vorschauansicht in der Haupt-App, statt die Systemplanung mit einem App-Timer gleichzusetzen.

Anhand der Symptome eingrenzen statt wiederholt neu installieren

Beobachtung Zuerst prüfen
Schon der in der Haupt-App zurückgelesene Wert ist falsch Zeitpunkt des Schreibens, Suite-Name und Fehlerbehandlung
Die Haupt-App liest den richtigen Wert, die Erweiterung noch den alten App-Group-Konfiguration beider Targets und Datensatzversion
Die Erweiterung liest den neuen Wert, die Einträge sind aber noch alt Übergabe der Parameter an den Provider und Aufbau der Timeline
Die neuen Einträge sind korrekt, die Anzeige bleibt aber alt Widget-Art, von der View verwendete Werte und tatsächlicher Anzeigezeitpunkt des Systems

Notiere vor der Fehlersuche Projekt-Commit, Runtime-Version, Geräte-ID und Revision des gemeinsamen Datensatzes. Diese Angaben sind aussagekräftiger als ein Screenshot mit unveränderter Anzeige. Lösche nicht nach jedem Fehlschlag sofort den gesamten Simulator: Dabei gehen auch die gemeinsamen Daten verloren, mit denen sich der Fehler reproduzieren lässt. Ist ein Zurücksetzen nötig, sichere zuerst die Testergebnisse und die erforderlichen bereinigten Protokolle. Entscheide dann gezielt, ob App-Daten gelöscht oder der Simulator neu erstellt werden soll.

Abnahmekriterien in die Pipeline übernehmen

Teile die Automatisierung in zwei Ebenen auf: Die erste führt Tests der reinen Funktion aus und fängt Regressionen bei Eintragsreihenfolge, Nullwert oder Revision ab. Die zweite prüft auf einem festgelegten Simulator, ob App und Erweiterung installiert sind und denselben Datensatz lesen. Das Layout wird manuell oder über eine kontrollierbare Vorschau überprüft. Die tatsächliche Aktualisierung des Widgets wird separat beobachtet und nicht mit den deterministischen Assertions der ersten Ebene vermischt.

Prüfe zum Schluss drei Dinge: Zeigen beide Targets auf dieselbe gemeinsame Gruppe? Tragen neue Daten eine nachvollziehbare Revision, ohne Inhalte preiszugeben? Wird die Zeit in Tests als Eingabe übergeben, statt auf das Verstreichen echter Minuten zu warten? Sind diese Bedingungen erfüllt, lässt sich eine erneut veraltete Anzeige der Reihe nach auf Daten, Einträge und Darstellung zurückführen – ohne die Ursache durch wiederholte Aktualisierungsanforderungen erraten zu müssen.

Häufig gestellte Fragen

Warum zeigt das Widget nach einer Aktualisierungsanforderung noch den alten Wert?

Die Anforderung garantiert keine sofortige Ausführung. Prüfen Sie zuerst den Wert im gemeinsamen Container und das Leseergebnis des Providers; bewerten Sie die Anzeige nicht anhand einer festen Sekundengrenze.

Muss ein Zeitplantest auf die Aktualisierung im Simulator warten?

Nein. Übergeben Sie Uhrzeit und Daten an eine reine Funktion und prüfen Sie Reihenfolge sowie Inhalt der Einträge. Der Simulator dient zur Kontrolle von Installation, Datenübergabe und Anzeige.

Exklusives Apple Silicon

Binde einen Cloud-Mac mit fester Konfiguration in deinen Workflow ein

Miete dedizierte physische Knoten tage-, wochen-, monats- oder quartalsweise und nutze feste Hardwareressourcen für Builds, Automatisierung und MLX-Inferenz.

Verfügbare Konfigurationen ansehen