Bei einer modernen Utility-App reicht eine großartige Oberfläche innerhalb der App nicht mehr aus. Der entscheidende Ort für langfristige Nutzung ist der Home-Bildschirm.
Als ich WaterPaw, meinen minimalistischen Trink-Tracker, entwickelte, waren native iOS-Widgets unverzichtbar. Nutzer sollten einen Blick auf den Home-Bildschirm werfen und ihre Trinkfortschritte sehen können. Wer mit React Native und Expo arbeitet, kennt jedoch die Herausforderungen: iOS-Widgets erfordern nativen Swift-Code, eigene App Extensions und Code-Signing-Ziele in Xcode. Manuelle Einrichtung durchbricht den nahtlosen Managed-Expo-Workflow.
So sieht die saubere, automatisierte Architektur aus, mit der ich native iOS-Widgets über Expo Config Plugins und gemeinsamen Local-First-Speicher veröffentlicht habe.
Die Architektur-Herausforderung: die Sandbox zwischen App und Widget
WaterPaw ist Local First und datenschutzorientiert, daher bleiben alle Daten auf dem Gerät. Ein iOS-Widget läuft jedoch in einem eigenen Hintergrundprozess des Betriebssystems und kann weder das normale AsyncStorage der App noch Dateien im Dokumentverzeichnis lesen.
Um diese Lücke ohne Cloud-Backend zu schließen, muss ein Apple-Sandbox-Container namens App Group eingerichtet werden:
[ WaterPaw React Native App ]
→ (Writes to Shared Suite)
→ [ iOS App Group Container ]
→ [ iOS Native Home Widget ] ← (Reads via Swift)Schritt 1: Daten aus React Native schreiben
Wenn Nutzer in der JavaScript-Ebene Wasser protokollieren, müssen wir die Daten in die gemeinsame App Group übertragen. react-native-shared-group-preferences kann die native Bridge-Anbindung übernehmen:
import SharedGroupPreferences from 'react-native-shared-group-preferences';
const APP_GROUP = 'group.com.yourgroupname';
export const syncDataToWidget = async (currentMl: number, goalMl: number) => {
const dataForWidget = {
progress: currentMl / goalMl,
displayString: `${currentMl} / ${goalMl} ml`,
updatedAt: new Date().toISOString()
};
try {
await SharedGroupPreferences.setItem('WidgetData', dataForWidget, APP_GROUP);
} catch (error) {
console.error('Failed to sync data to iOS App Group Suite', error);
}
};Schritt 2: Gemeinsame Daten in Swift verwenden
Im nativen Swift-Widget-Verzeichnis kannst du dieselbe App-Group-Suite sofort über UserDefaults(suiteName:) auslesen. So bleibt alles lokal, schnell und zu 100 % privat:
import WidgetKit
import SwiftUI
struct HydrationEntry: TimelineEntry {
let date: Date
let progress: Double
let displayString: String
}
struct WaterPawWidgetEntryView: View {
var entry: Provider.Entry
var body: some View {
VStack(alignment: .leading, spacing: 8) {
Text("WaterPaw")
.font(.caption)
.foregroundColor(.secondary)
Text(entry.displayString)
.font(.title2)
.bold()
ProgressView(value: entry.progress)
.progressViewStyle(LinearProgressViewStyle(tint: .blue))
}
.padding()
}
}Schritt 3: Xcode mit Expo Config Plugins automatisieren 🛠️
Hier geraten Entwickler häufig in Schwierigkeiten. Wenn du npx expo prebuild ausführst, entfernt Expo alle manuellen Xcode-Änderungen und Widget-Ziele.
Für einen vollständig verwalteten Workflow legen wir den Swift-Code in einem eigenen targets/-Verzeichnis ab und nutzen ein Config Plugin wie expo-target-extension, um den nativen Aufbau zu automatisieren. Expo erstellt damit jedes Mal automatisch das Widget-Ziel, verknüpft die Swift-Kompilierungsphasen und fügt die App-Group-Berechtigungen ein:
{
"expo": {
"name": "WaterPaw",
"slug": "waterpaw",
"plugins": [
[
"expo-target-extension",
{
"targetName": "WaterPawWidget",
"dir": "targets/widgets",
"entitlements": {
"com.apple.security.application-groups": ["group.com.yourgroupname"]
}
}
]
]
}
}Wenn diese Konfiguration in app.json liegt, das ios/-Verzeichnis gelöscht und npx expo prebuild --clean ausgeführt wird, wird die native Xcode-Umgebung vollständig eingerichtet.
Fazit: Nicht aussteigen, automatisieren
Wenn native Erweiterungs-Workflows vollständig in Config Plugins liegen, bleibt das Mobilprojekt sauber und zukunftssicher. Du behältst die schnellen Entwicklungszyklen von Expo und kannst Nutzern gleichzeitig eine tief integrierte iOS-Systemfunktion ohne Verzögerung bieten.
Bei Utility-Apps zahlt sich der zusätzliche Aufwand, Daten auf dem Home-Bildschirm schnell erfassbar zu machen, langfristig durch mehr Engagement aus. Mit dieser Architektur habe ich die Fortschrittsringe und die Maskottchen-Anzeige in WaterPaw umgesetzt.
