iOS-Widgets
React-Native-Widgets ohne doppelte Geschäftslogik in Swift
Ein versionierter JSON-Schnappschuss lässt WidgetKit TypeScript-Berechnungen nutzen, Fristen fortschreiben und Datenschutzregeln einhalten.
Aktualisiert am
In diesem Artikel
„Nächste Wartung“ ist eine kurze Beschriftung mit erstaunlich vielen Abhängigkeiten.
In OdoKeep hängt sie unter anderem von Fahrzeug, Wartungshistorie, Entfernungseinheit, aufgeschobenen Hinweisen und aktueller Prognose ab. Monatsausgaben haben andere Regeln, etwa die Umrechnung eines Fremdwährungsbelegs zum Datum dieses Eintrags.
Diese Antworten existierten bereits in TypeScript. iOS-Widgets wären eine gute Gelegenheit gewesen, sie nach Swift zu kopieren und das nächste Jahr mit widersprüchlichen Ergebnissen zu verbringen.
Stattdessen schreibt die App die fertigen Antworten auf.
OdoKeep nutzt React Native und Expo und bietet vier WidgetKit-Widgets: Fahrzeug, Fristen, Monatsausgaben und Wartung. Die Erweiterung ist ein eigener Prozess. Sie führt kein React Native aus und öffnet nicht den MMKV-Speicher der App.
Die Grenze bildet ein JSON-Schnappschuss in einem App-Group-Container. Die TypeScript-App erstellt ihn mit den Dashboard-Modulen. Swift decodiert ihn und zeichnet die Ansicht.
Fertige Antworten über die Prozessgrenze senden
Der Schnappschuss ist ein Darstellungsvertrag, kein Rohdatenexport. Beträge sind formatiert, Einheiten angewendet, Beschriftungen übersetzt und Deep Links aufgebaut. Sogar die Ausgabenbalken enthalten fertige Beschriftungen und normierte Verhältnisse.
Dieser Ausschnitt zeigt die Struktur:
interface WidgetSpendBucket {
id: string;
label: string;
ratio: number;
isCurrent: boolean;
}
interface WidgetDeadline {
id: string;
label: string;
vehicleId: string;
vehicleName: string;
dueDate: string;
dueDateLabel: string;
warningFrom: string;
url: string;
}
Nützliche Struktur bleibt erhalten. Swift braucht Identitäten für die Fahrzeugauswahl, Daten zum Fortschreiben der Anzeige und Verhältnisse für Balken. Es braucht keine zweite Implementierung der Finanz- oder Wartungsregeln.
Dashboard-Hinweise, Wartungsprognosen und Ausgabenberechnung speisen lib/widgets/snapshot.ts. plugins/widgets/WidgetsSnapshot.swift bildet den Decodervertrag ab. Ändert sich die Bedeutung eines Felds, ändert sich die gemeinsame Version. Versteht der Decoder diese Version nicht, bittet er darum, die App zu öffnen.
Das lässt sich leichter untersuchen als ein Widget, das selbstsicher eine falsche Interpretation anzeigt.
Den Kalender ohne App weiterlaufen lassen
Der Kalender ist die bewusste Ausnahme von vorberechneten Antworten. Ein Widget bleibt möglicherweise tagelang sichtbar, ohne dass die App geöffnet wird. „In drei Tagen“ muss nach Mitternacht weiterzählen können.
Deshalb speichert der Schnappschuss die Frist als lokalen Kalendertag, einen warningFrom-Tag und fertige übersetzte Texte nach Tagesabstand:
"0" -> Heute
"1" -> Morgen
"2" -> In 2 Tagen
"-2" -> Seit 2 Tagen überfällig
Die echte Tabelle nutzt die Übersetzungsfunktion der App, reicht sechzig Tage zurück und 120 voraus und enthält einen Ersatztext für ältere Überschreitungen. Swift zählt Kalendertage und schlägt die fertige Zeichenfolge nach. Es kann am vorberechneten Warntag den Zustand ändern, ohne die Entscheidungslogik für dieses Datum zu kennen.
Entfernungsabhängige Wartung bekommt stattdessen eine feste Beschriftung. Eine vergangene Nacht erhöht keinen Kilometerstand.
WidgetTimeline.swift erstellt einen Eintrag für jetzt und einen für jede lokale Mitternacht der nächsten Woche mit .atEnd als Aktualisierungsregel. Der Schnappschuss bleibt gleich, das Datum des Eintrags ändert sich. Das liefert künftige Zustände an WidgetKit, verspricht aber keine Ausführung zu einer exakten Uhrzeit. WidgetKit kontrolliert die Planung, wie Apples Dokumentation zur Widget-Aktualisierung erläutert.
Erst die Datei ersetzen, dann neu laden lassen
Der Hauptschreibvorgang erfolgt, wenn die App den Vordergrund verlässt, kurz vor der Rückkehr zum Home-Bildschirm. Ein Vier-Sekunden-Timer nach dem Mounten erzeugt einen ersten Stand, falls die Sitzung noch nicht in den Hintergrund gewechselt ist. Beobachtbare Einstellungsänderungen starten ihn erneut.
Das Repository bietet kein Abonnement für jeden geschriebenen Eintrag. Nach Ablauf des Timers hinzugefügte Daten erreichen den Schnappschuss daher erst beim nächsten Hintergrundwechsel. Das ist die Aktualitätsgrenze. Eine Änderung an einem geteilten Fahrzeug kann das Widget ebenfalls erst kennen, nachdem die App sie abgerufen hat.
Der Writer verwendet dort synchrone Dateioperationen. Er schreibt das ganze JSON unter einem temporären Namen und verschiebt es anschließend mit Überschreiben auf die echte Datei. So liest der normale Decoderpfad kein halb geschriebenes Dokument. Eine von einem Abbruch übrig gebliebene temporäre Datei wird vor Wiederverwendung gelöscht.
Erst danach fordert ein kleines natives Expo-Modul das Neuladen der WidgetKit-Timelines an. Neue Bytes allein ersetzen die bestehende Timeline nicht. Das Neuladen bleibt eine Bitte an den Scheduler und hängt von iOS sowie erfolgreichem Schreiben ab.
Datenschutz gehört in den Ersteller des Schnappschusses
Die Erweiterung erhält weniger Daten, als die App kennt. Bei aktivierter biometrischer Sperre enthält die Datei keine Kilometerstände oder Geldwerte. Die Felder sind null; sensible Werte werden gar nicht serialisiert, statt nur später versteckt zu werden. Das Kennzeichen wird unabhängig vom Sperrzustand nie aufgenommen. Fristen und Wartungsinformationen bleiben verfügbar.
Swift-Ansichten markieren sensible Werte außerdem für die systemseitige Ausblendung. Das ergänzt die iOS-Anzeigekontrollen, während der Ersteller entscheidet, was die Erweiterung überhaupt erreicht.
Abmelden und Kontolöschung entfernen die Datei und fordern eine Aktualisierung an. Das gehört zum Sitzungsende, nicht bloß zum Unmounten des schreibenden Hooks. Sonst könnte ein gespeicherter Stand sein ursprüngliches Konto überleben.
Dasselbe Prinzip für Siri und Kurzbefehle
Lesende Siri- und Kurzbefehle-Intents verwenden eine separate Darstellungsdatei im Documents-Verzeichnis der App. Sie werden in das App-Target kompiliert, nicht in die WidgetKit-Erweiterung. Die Ersteller teilen Eingaben, doch Dateien und Verbraucher haben eigene Verträge.
Ein schreibender Kurzbefehl öffnet einen Deep Link in ein bestehendes Formular. Er verändert nicht aus Swift heraus den Speicher. Die Route prüft weiterhin Freigaberolle, Tarifgrenzen, Kilometerstand und Synchronisierung. Siri-Parameter füllen eine Anfrage vor, umgehen aber keinen Schreibpfad.
Mit Expo prebuild reproduzierbar bleiben
Das Projekt erzeugt sein von Git ignoriertes iOS-Verzeichnis per Expo prebuild neu. Ein manuell in Xcode hinzugefügtes Target würde verschwinden. plugins/with-widgets.js erzeugt und konfiguriert deshalb die Erweiterung, verknüpft Swift-Dateien und Ressourcen, deklariert App Group und beschreibt EAS das zweite Target für die Signierung. Das folgt dem Config-Plugin-Modell von Expo für reproduzierbare native Anpassungen.
Eine Einstellung verdient Erklärung: ENABLE_DEBUG_DYLIB = NO für die Erweiterung. Die Implementierung dokumentiert einen Fehler, bei dem die App-Intents-Metadatenextraktion die Abhängigkeiten des Debug-Stubs untersuchte, AppIntents nicht fand und die Extraktion ohne Buildfehler übersprang. Konfigurierbare Widgets hatten dann keinen nutzbaren Intent und blieben trotz angemeldeter App leer. Die Einstellung erzeugt das Binärlayout, das dieser Metadatenpfad erwartet.
Tests prüfen den reinen Ersteller, erforderliche Swift-Decoderfelder, Deep Links, Übersetzungstabellen, gemeinsame Farben, Schriften und native Konfiguration. Das sind gezielte Vertragsprüfungen, kein generiertes sprachübergreifendes Schema und kein Ersatz für Gerätetests. Sie fangen Abweichungen zwischen separat kompilierten Verbrauchern ab, bevor diese auf dem Home-Bildschirm ankommen.
Diese Grenze würde ich wiederverwenden: Die App verantwortet die Bedeutung ihrer Daten und liefert eine begrenzte, versionierte Beschreibung der Darstellung. Daten behalten genug Struktur, um weiterzuzählen; die Aktualität aller anderen Antworten folgt ausdrücklich dem letzten Schreibvorgang.
Die vier Widgets sind in OdoKeep für iOS umgesetzt. Sie zeigen Fristen und nächste Wartungen, ohne das ganze Fahrzeugtagebuch zu öffnen.