iOS stoppt Background-Location-Updates, weil es deine App suspendiert, nicht weil Core Location fehlschlägt. Wenn die App den Location Background Mode nicht deklariert, Background-Updates nicht CLLocationManageraktiviert und diese nicht startet, während die App noch im Vordergrund ist, friert iOS den Prozess wenige Sekunden nachdem der Nutzer die App verlässt ein und stellt die Location-Fixes in die Warteschlange oder verwirft sie. Dieser Guide listet alle Ursachen, die wir in Capacitor Apps sehen, mit der jeweiligen Lösung auf und zeigt, wie @capgo/background-geolocation verarbeitet sie.
Wie iOS mit einer App im Hintergrund umgeht
Wenn ein Nutzer nach Hause wischt oder das Telefon sperrt, gewährt iOS der App ein kurzes Zeitfenster, bevor sie suspendiert wird. Eine suspendierte App erhält keine CPU-Zeit. Für eine Capacitor-App bedeutet das, dass sowohl die native Seite als auch die WebView aufhören zu laufen, sodass auch dein JavaScript-Callback nicht mehr feuert.
Apple macht eine Ausnahme für Apps, die aktiv den Standort nutzen. Wenn die App für Hintergrund-Standort konfiguriert ist und eine aktive Standort-Session im Vordergrund gestartet wurde, lässt iOS sie laufen und liefert weiterhin Updates. Alles Folgende dreht sich darum, diesen Vertrag einzuhalten – und um die Fälle, in denen das System ihn absichtlich bricht.
Ursache 1: Der Location-Background-Mode fehlt
Ohne location in einem UIBackgroundModes, egal wie viel code du nutzt, werden die Updates nicht fließen. Füge es hinzu ios/App/App/Info.plist:
<key>UIBackgroundModes</key>
<array>
<string>location</string>
</array>
Du kannst es auch in Xcode unter Signing & Capabilities, Background Modes, Location updates aktivieren. Es ist ein plist-Key, kein Entitlement, daher ist keine Änderung am Provisioning-Profil nötig.
Die native Eigenschaft dahinter ist allowsBackgroundLocationUpdates. Wenn man sie auf true setzt, ohne den Hintergrundmodus im plist zu aktivieren, stürzt die App ab. Das Capgo-Plugin übernimmt das für dich, wenn du backgroundMessage zu start(), daher muss der plist-Key vor dem Release vorhanden sein.
App Review prüft, ob der Background-Modus gerechtfertigt ist. Erkläre in den Review-Notizen, welche Funktion ihn nutzt, und stelle sicher, dass die Funktion für den Reviewer sichtbar ist.
Ursache 2: Updates wurden im Hintergrund gestartet
Apples Regel: Background-Updates laufen weiter, wenn sie gestartet wurden, während die App im Vordergrund war. Der Versuch, eine Session über einen Silent Push, einen Background Fetch oder einen BGTaskScheduler Task zu starten, funktioniert bei Standard-Location-Updates nicht.
Starte das Tracking durch eine Nutzeraktion oder beim App-Resume:
import { App } from '@capacitor/app';
import { BackgroundGeolocation } from '@capgo/background-geolocation';
let tracking = false;
export async function startTracking() {
if (tracking) return;
tracking = true;
await BackgroundGeolocation.start(
{
backgroundMessage: 'Recording your route',
backgroundTitle: 'Route recording',
requestPermissions: true,
distanceFilter: 10,
url: 'https://api.example.com/v1/points',
},
(location, error) => {
if (error) return console.error(error);
if (location) console.log(location.latitude, location.longitude);
},
);
}
// Re-arm when the user comes back, if the session should be active
App.addListener('appStateChange', ({ isActive }) => {
if (isActive && shouldBeTracking()) startTracking();
});
declare function shouldBeTracking(): boolean;
Ursache 3: backgroundMessage wurde nicht übergeben
Das betrifft speziell das Capgo-Plugin und ist der häufigste Bugreport. Wenn backgroundMessage undefined ist, konfiguriert das Plugin den Location Manager nur für die Nutzung im Vordergrund: allowsBackgroundLocationUpdates bleibt false und Updates stoppen, wenn die App suspendiert wird. Übergebe eine Nachricht auf beiden Plattformen. Auf Android wird sie zusätzlich zum Text der Foreground-Service-Benachrichtigung.
Ursache 4: falsche Berechtigungsstufe oder „Einmal erlauben“
Viele Entwickler glauben, sie bräuchten Always, um im Hintergrund zu tracken. Das ist nicht der Fall. Apple dokumentiert, dass eine App mit When In Use-Autorisierung im Hintergrund weiterläuft, solange aktive Location-Dienste aktiv sind und Background-Updates enabled sind.
Always ist für etwas anderes relevant: das Neustarten einer terminierten App. Nur Region Monitoring, Significant-Change-Location und Visits können eine App neu starten – und nur mit Always.
Zwei Benutzerentscheidungen brechen das Background-Tracking stillschweigend:
- Einmal erlauben. Dies ist eine temporäre When In Use-Gewährung, die abläuft, wenn die App nicht mehr genutzt wird. Die nächste Session startet wieder bei „nicht bestimmt“.
- Während der Nutzung, dann wird der Always-Prompt nie angezeigt. Wenn du Always anforderst, kann iOS den Upgrade-Prompt später zu einem von ihm gewählten Zeitpunkt anzeigen, oft wenn die App nicht im Vordergrund ist.
Prüfe den tatsächlichen Status, bevor du eine Session startest:
import { BackgroundGeolocation } from '@capgo/background-geolocation';
const status = await BackgroundGeolocation.checkPermissions();
if (status.location !== 'granted') {
await BackgroundGeolocation.requestPermissions({ permissions: ['location'] });
}
if (status.backgroundLocation === 'when_in_use') {
// Background tracking still works for standard updates.
// Explain why Always helps if you also rely on geofences.
}
Unter iOS, backgroundLocation gibt zurück when_in_use oder granted / always, damit du die passende Erklärung anzeigen kannst, statt zu raten.
Ursache 5: Core Location pausiert Updates automatisch
CLLocationManager.pausesLocationUpdatesAutomatically ist standardmäßig true. Wenn iOS entscheidet, dass sich der Nutzer nicht bewegt, pausiert es die Updates, um Akku zu sparen. Bei einer App mit „When In Use“ beendet eine Pause den Standortzugriff, bis die App erneut gestartet wird; Core Location setzt ihn nicht von selbst fort.
Das Capgo-Plugin setzt pausesLocationUpdatesAutomatically = false in seinem nativen code, daher ist dieser Fall abgedeckt. Wenn du deinen eigenen nativen code schreibst oder ein anderes Plugin verwendest, prüfe zuerst diese Eigenschaft.
Ursache 6: Das Gerät steht still und der Filter ist groß
Mit einem distanceFilter von 50 Metern, ein auf dem Schreibtisch liegendes Telefon erzeugt keine Updates. Das sieht in deinen Logs aus wie „Tracking gestoppt“, funktioniert aber wie vorgesehen.
- Wenn du Heartbeats im stationären Zustand brauchst, nutze einen kleinen oder null
distanceFilter. - Nutzt
minIntervalMsum zu begrenzen, wie oft native POSTs gesendet werden, statt einen großen Distanzfilter zu nutzen.
await BackgroundGeolocation.start(
{
backgroundMessage: 'Vehicle tracking active',
distanceFilter: 0,
minIntervalMs: 120_000, // at most one native POST every 2 minutes
url: 'https://api.example.com/v1/vehicle-points',
},
() => {},
);
Ursache 7: „Genaue Standortbestimmung“ ist deaktiviert
Seit iOS 14 können Nutzer den präzisen Standort für deine App deaktivieren. Du bekommst zwar weiterhin Updates, diese sind aber ungenau (mehrere Kilometer) und kommen viel seltener. Das erkennst du an deinen Daten (große accuracy Werte) und erklärst dem Nutzer, warum der genaue Standort für dein Feature wichtig ist. Wenn du eine Liefer- oder Fitness-App baust, gehört das ins Onboarding – nicht erst nach einer misslungenen Fahrt.
Ursache 8: Der Nutzer hat die App beendet
Wenn der Nutzer die App im App-Switcher wegwischt, stoppt iOS die Standard-Standort-Updates. Kein Plugin kann sie am Laufen halten. Das überrascht Teams, die von Android kommen, wo ein Foreground Service auch nach dem Entfernen der Task weiterläuft.
Optionen:
- Sag den Nutzern, dass sie die App während einer Session nicht beenden sollen. Die meisten Fleet-Apps machen das im Onboarding.
- Richte Geofences mit „Immer“-Berechtigung um wichtige Orte ein, damit iOS die App im Hintergrund beim Betreten oder Verlassen neu starten kann.
- Sende einen Push, um den Nutzer daran zu erinnern, die App wieder zu öffnen, wenn dein Server für eine Session keine Punkte mehr empfängt.
Ursache 9: JavaScript läuft nicht, obwohl der native Code läuft
Ein subtiles Detail: Natives code kann einen Standort empfangen, während die WebView pausiert ist oder zwar noch läuft, aber Timer oder Netzwerkaufrufe nicht schnell verarbeitet. fetch vom Callback aus können sie verspätet oder gar nicht ankommen.
Nutzen Sie die url Jeder Punkt wird per POST aus dem nativen code mit demselben JSON wie der Location Typ plus "source": "native", unabhängig vom WebView. Nutze den Callback nur für UI-Updates. Die Zustellung ist Best Effort ohne On-Disk-Queue. Wenn du keinen Datenpunkt verlieren darfst, speichere ihn zusätzlich lokal, wenn der Callback ausgelöst wird, und gleiche ihn später ab.
Für Hintergrundarbeit außerhalb von Location lies, wie Background Tasks in Capacitor funktionieren, und sehen Sie @capgo/capacitor-background-task.
Ursache 10: Stromsparmodus und thermischer Zustand
Der Stromsparmodus reduziert die Hintergrundaktivität systemweit. Die Standortbestimmung funktioniert weiterhin, aber andere Dinge, von denen deine Session abhängt (Network Retries, Background Refresh), werden langsamer. Teste mit aktiviertem Stromsparmodus, bevor du ein Release freigibst.
Der blaue Indikator
Wenn eine App mit der Berechtigung „Beim Verwenden der App“ den Standort im Hintergrund nutzt, zeigt iOS ein blaues Symbol in der Statusleiste an. Das Capgo Plugin setzt showsBackgroundLocationIndicator zu true für Background-Sessions, genau das, was Nutzer erwarten und worauf Reviewer achten. Versuche nicht, es zu verstecken – Nutzer vertrauen darauf.
Ein Testplan, der diese Bugs abfängt
Simulator-Tests reichen für Background-Location nicht aus. Nutze ein echtes iPhone und:
- Erstelle eine Release-Konfiguration, nicht nur Debug. Debug-Builds, die an Xcode hängen, werden im Lifecycle anders behandelt.
- Starte eine Session im Vordergrund, sperre das Handy und gehe oder fahre 20 Minuten lang.
- Wiederhole das mit „Einmal erlauben“, mit deaktiviertem „Genauer Standort“ und mit aktiviertem „Stromsparmodus“.
- Prüfe deine Server-Logs auf
source: "native"Datenpunkte, nicht nur dein In-App-Log. - Erzwinge das Beenden während einer Session und prüfe, ob deine App beim nächsten Start erklärt, was passiert ist.
Falls du eine Device-UDID für Ad-hoc-Builds benötigst, ist die iOS UDID Finder bezieht es von Safari. Falls du keinen Mac mit Xcode 26 für Builds hast, Capgo Build kann signierte iOS-Builds in der Cloud erstellen.
Troubleshooting-Checkliste
| Symptom | Wahrscheinliche Ursache | Lösung |
|---|---|---|
| Stoppt Sekunden nach dem Sperren | Nein location Background-Mode oder fehlend backgroundMessage |
Plist-Key hinzufügen und übergeben backgroundMessage |
| Crash beim Start | allowsBackgroundLocationUpdates ohne Hintergrundmodus |
Füge UIBackgroundModes → location |
| Funktioniert einmal, dann nicht mehr | Nutzer hat „Einmal erlauben“ gewählt | Erklären und erneut fragen, oder senden an openSettings() |
| Stopp bei Parken | Groß distanceFilter |
Senken, verwenden minIntervalMs |
| Große Abstände, geringe Genauigkeit | Präzise Position deaktiviert | Nutzer zur Aktivierung auffordern |
| Stoppt nach Wischgeste | Nutzer hat die App beendet | Auf iOS zu erwarten; Geofences für den Relaunch nutzen |
| Native bekommt Punkte, der Server nicht | JavaScript fetch im Callback |
Nutze das url Option |
Fixes ausrollen
Die meisten dieser Fixes betreffen Info.plist oder native Konfiguration, sodass ein neuer App Store-Build erforderlich ist. Die JavaScript-Teile, wie Erläuterungen zu Berechtigungen, Onboarding-Texte und Retry-Logik, können mit einem Live Update an bestehende Nutzer ausgeliefert werden. Capgo Live Updates sobald der Native Build verfügbar ist.
Wenn du dich zwischen Plugins entscheiden musst, zeigt unser Vergleich von Transistorsoft und dem kostenlosen Capgo Plugin alle Vor- und Nachteile auf.