Skip to content

Live Activities

I767513 edited this page May 10, 2026 · 2 revisions

Live Activities & Dynamic Island

Sobald eine Fahrt gestartet wird, übernimmt die Dynamic Island – Echtzeit-Updates ohne die App zu öffnen.


Inhaltsverzeichnis

  1. Übersicht & Voraussetzungen
  2. UI-Bereiche
  3. Phasen einer Fahrt
  4. Datenmodell
  5. Update-Zyklus
  6. Hintergrund-Updates
  7. App Group Datenaustausch
  8. Activity beenden
  9. Testen

1. Übersicht & Voraussetzungen

Feature Mindestanforderung
Live Activity (Sperrbildschirm) iOS 16.2, beliebiges iPhone
Dynamic Island (kompakt) iPhone 14 Pro / Pro Max
Dynamic Island (erweitert) iPhone 14 Pro / Pro Max
StandBy-Anzeige iOS 17, iPhone mit Always-On

Live Activities müssen vom Nutzer nicht extra aktiviert werden – sie erscheinen automatisch, sobald eine Fahrt über die App gestartet wird. Der Nutzer kann sie unter Einstellungen → ÖPNV Mannheim → Live-Aktivitäten deaktivieren.


2. UI-Bereiche

┌─────────────────────────────────────────────┐
│                Dynamic Island                │
│                                              │
│  ┌──────────────────────────────────────┐   │
│  │  Kompakt (Ruhezustand)               │   │
│  │  🚋 RNV 5 ──────────── 3 Min.        │   │
│  └──────────────────────────────────────┘   │
│                                              │
│  ┌──────────────────────────────────────┐   │
│  │  Erweitert (bei Berührung)           │   │
│  │  Linie: RNV 5 → Heidelberg           │   │
│  │  Nächster Halt: Wasserturm           │   │
│  │  Ankunft: 14:32  Verspätung: +2 Min. │   │
│  │  ████████░░░░░░░░  60%               │   │
│  └──────────────────────────────────────┘   │
└─────────────────────────────────────────────┘

┌─────────────────────────────────────────────┐
│              Sperrbildschirm                 │
│                                              │
│  🚋 RNV 5 → Heidelberg                      │
│  Aktuell: Paradeplatz                        │
│  Nächster Halt: Wasserturm  14:32            │
│  ████████████░░░░░░  Verspätung: +2 Min.     │
│                                    [Beenden] │
└─────────────────────────────────────────────┘

3. Phasen einer Fahrt

BEFORE_DEPARTURE          IN_PROGRESS              ARRIVED
      │                        │                       │
      ▼                        ▼                       ▼
Abfahrt in X Min.       Nächster Halt:          Ziel erreicht!
Linie / Gleis           Station Y, Z Min.       Activity endet
                        Verspätung              nach 30 Sek.
                        Fortschrittsbalken

Die App wechselt Phasen automatisch:

  • → IN_PROGRESS: Wenn die tatsächliche Abfahrtszeit erreicht ist
  • → ARRIVED: Wenn die Ankunftszeit am Ziel erreicht ist (+ optionale Echtzeit-Bestätigung)

4. Datenmodell

struct TripLiveActivityAttributes: ActivityAttributes {

    // Statische Daten (ändern sich nicht)
    let tripId: String
    let line: String            // "RNV 5"
    let destination: String     // "Heidelberg Bismarckplatz"

    // Dynamischer Zustand (wird bei jedem Update überschrieben)
    struct ContentState: Codable, Hashable {
        var currentStop: String         // Aktueller Halt
        var nextStop: String            // Nächster Halt
        var nextStopTime: Date          // Abfahrtszeit nächster Halt
        var remainingTime: TimeInterval // Sekunden bis nächster Halt
        var delay: Int                  // Verspätung in Minuten (0 = pünktlich)
        var occupancy: OccupancyLevel   // Auslastung
        var phase: TripPhase            // .beforeDeparture / .inProgress / .arrived
        var progress: Double            // 0.0 – 1.0 für Fortschrittsbalken
    }
}

enum TripPhase: String, Codable {
    case beforeDeparture
    case inProgress
    case arrived
}

5. Update-Zyklus

Der LiveActivityManager passt die Update-Frequenz dynamisch an die verbleibende Zeit an:

Zustand Intervall
Abfahrt > 5 Minuten entfernt alle 30 Sekunden
Abfahrt < 5 Minuten entfernt alle 10 Sekunden
Unterwegs, nächster Halt > 1 Min. alle 15 Sekunden
Unterwegs, nächster Halt < 1 Min. alle 5 Sekunden
Ankunft erreicht einmalig → Activity beenden

ActivityKit erlaubt maximal ~15 Updates pro Stunde aus dem Hintergrund. Die Update-Frequenz ist bewusst so gewählt, dass dieses Limit nicht überschritten wird.


6. Hintergrund-Updates

Wenn die App in den Hintergrund wechselt oder gesperrt wird, übernimmt das BackgroundTasks-Framework die Updates:

App in Hintergrund
       │
       ▼
BGTask: "com.stefanfriedrich.rnvapp.liveactivity-update"
       │
       ▼
LiveActivityManager.performBackgroundUpdate()
       │
       ├── GraphQLService.fetchRealtime(tripId)
       │
       └── Activity.update(using: newContentState)
              │
              └── Nächsten BGTask einplanen

Der BGTask wird in RNV_Transport_AppApp.swift registriert und nach jedem Update erneut eingeplant.


7. App Group Datenaustausch

Live Activity Extension, iOS-App und Watch teilen Fahrtdaten über eine gemeinsame App Group:

iOS App (LiveActivityManager)
    │
    ▼ schreibt
UserDefaults(suiteName: "group.com.stefanfriedrich.rnvapp")
    │
    ├── liest: RNVLiveActivity Extension
    └── liest: RNVWatch (WatchDataManager)

8. Activity beenden

Eine laufende Live Activity kann auf mehrere Arten beendet werden:

Methode Wer
Automatisch bei Ankunft LiveActivityManager
Button in TripDetailView Nutzer in der App
Long Press auf Sperrbildschirm → Beenden Nutzer
Siri: „Beende ÖPNV Mannheim" EndAllActivitiesIntent
Nach 8 Stunden (iOS-Limit) iOS System

9. Testen

Live Activities können nur auf echten Geräten getestet werden (kein Simulator-Support).

Voraussetzungen:

  • iPhone 14 Pro oder neuer (für Dynamic Island)
  • iOS 16.2+ (für Sperrbildschirm Live Activity)
  • Live Activities in Einstellungen → ÖPNV Mannheim aktiviert

Tipps:

  • In Xcode: Schema Mannheim ÖPNV auf echtes Gerät deployen
  • Live Activity startet automatisch, wenn eine Verbindung über die App gestartet wird
  • Fortschritt im Debugger beobachten: LiveActivityManager loggt alle State-Updates

Clone this wiki locally