Skip to content

Architektur

I767513 edited this page May 10, 2026 · 2 revisions

Architektur

Die App folgt dem MVVM-Muster (Model – View – ViewModel) mit SwiftUI und ist in drei Build-Targets aufgeteilt.


Inhaltsverzeichnis

  1. Targets & Verantwortlichkeiten
  2. Ordnerstruktur
  3. Schichtenarchitektur
  4. Datenfluss
  5. State Management
  6. Datenpersistenz & App Groups
  7. Sicherheit
  8. Swift Packages

1. Targets & Verantwortlichkeiten

┌─────────────────────────────────────────────────────────┐
│                    Mannheim ÖPNV.xcodeproj               │
│                                                           │
│  ┌──────────────────┐  ┌────────────┐  ┌─────────────┐  │
│  │  Mannheim ÖPNV   │  │  RNVWatch  │  │RNVLiveActiv.│  │
│  │   (iOS 16.2+)    │  │(watchOS10+)│  │ (iOS 16.2+) │  │
│  └──────────────────┘  └────────────┘  └─────────────┘  │
│           │                  │                │           │
│           └──── App Group ───┴────────────────┘           │
│         (group.com.stefanfriedrich.rnvapp)                │
└─────────────────────────────────────────────────────────┘
Target Plattform Aufgabe
Mannheim ÖPNV iOS 16.2+ Haupt-App: Verbindungssuche, Abfahrten, Einstellungen
RNVWatch watchOS 10+ Watch-App: Abfahrten, aktive Fahrt, Komplikation
RNVLiveActivity iOS 16.2+ Live Activity & Dynamic Island Extension

2. Ordnerstruktur

RNV-Transport-App/
│
├── RNV-Transport-App/               ← Haupt-App
│   ├── RNV_Transport_AppApp.swift   ← Einstiegspunkt, BGTask-Registrierung
│   ├── AppConfiguration.swift       ← App Group ID, Feature Flags, Timeouts
│   ├── SharedModels.swift           ← Geteilte Datenstrukturen
│   │
│   ├── Content/                     ← SwiftUI Views
│   │   ├── ContentView.swift        ← Tab-Container (4 Tabs)
│   │   ├── ConnectionsView.swift    ← Verbindungssuche mit Filter
│   │   ├── DepartureBoardView.swift ← Echtzeit-Abfahrtstafel
│   │   ├── TripDetailView.swift     ← Streckenabschnitte, Karte
│   │   ├── PlannedTripsView.swift   ← Gespeicherte Fahrten
│   │   ├── SettingsView.swift       ← Radius, Filter, Dark Mode
│   │   ├── StationPickerView.swift  ← Suche + GPS-Erkennung
│   │   ├── TransitMapSheet.swift    ← Kartenansicht
│   │   └── OnboardingView.swift     ← Erststart
│   │
│   └── Services/                    ← Business Logic
│       ├── GraphQLService.swift          ← RNV API Client (Apollo)
│       ├── SecureGraphQLService.swift    ← Wrapper mit Token-Validierung
│       ├── AuthService.swift             ← Azure AD Token Management
│       ├── LocationManager.swift         ← CoreLocation / GPS
│       ├── LiveActivityManager.swift     ← ActivityKit Lifecycle
│       ├── TripDataManager.swift         ← Persistenz via App Groups
│       ├── PhoneConnectivityManager.swift← WatchConnectivity (iPhone-Seite)
│       ├── NetworkMonitor.swift          ← Netzwerkstatus (Combine)
│       └── EncryptionService.swift       ← Credential-Verschlüsselung
│
├── RNVWatch/                        ← Apple Watch App
│   ├── WatchApp.swift
│   ├── WatchConnectivityManager.swift   ← WatchConnectivity (Watch-Seite)
│   ├── WatchDataManager.swift           ← Lokaler Cache
│   ├── WatchDirectService.swift         ← Direkte API-Calls ohne iPhone
│   ├── WatchModels.swift
│   └── Views/
│       ├── ContentView.swift
│       ├── ActiveTripView.swift
│       ├── DeparturesView.swift
│       └── SavedTripsView.swift
│
├── RNVLiveActivity/                 ← Live Activity Extension
│   ├── RNVLiveActivityLiveActivity.swift
│   ├── WatchTripWidget.swift
│   ├── HomeScreenWidgets.swift
│   ├── Components/LiveActivityComponents.swift
│   └── Helpers/LiveActivityHelpers.swift
│
├── docs/                            ← Marketing-Website (GitHub Pages)
├── Debug.xcconfig
├── Release.xcconfig
└── Secrets.xcconfig                 ← API-Credentials (nicht im Repo)

3. Schichtenarchitektur

┌─────────────────────────────────────┐
│              Views (SwiftUI)         │  ← Darstellung, kein Business Logic
├─────────────────────────────────────┤
│         ViewModels / Services        │  ← @ObservableObject, Combine
│  AuthService · GraphQLService        │
│  LocationManager · LiveActivityMgr.  │
├─────────────────────────────────────┤
│              Models                  │  ← Codable Structs
│  Station · DetailedTrip · TripLeg    │
│  OccupancyLevel · IntermediateStop   │
├─────────────────────────────────────┤
│         Persistenz / System          │  ← UserDefaults, App Groups
│  TripDataManager · EncryptionService │
└─────────────────────────────────────┘

4. Datenfluss

RNV GraphQL API  ←──────────────────────────────────┐
       │                                              │
       ▼                                              │
 GraphQLService                                   DirectService
 (AuthService holt                                (Watch, offline)
  Bearer Token)
       │
       ├──► ConnectionsView       (Verbindungssuche)
       ├──► DepartureBoardView    (Abfahrten)
       └──► TripDetailView        (Details)
                  │
                  ▼
          LiveActivityManager ──► Dynamic Island
                  │                  Sperrbildschirm
                  ▼
         TripDataManager ──► App Group UserDefaults
                  │
                  ▼
        PhoneConnectivityManager ──► WatchConnectivityManager
                                           │
                                           ▼
                                    Watch Views

5. State Management

Services werden als @EnvironmentObject in den View-Baum injiziert. Die App verwendet kein externes State-Management-Framework.

// App-Einstiegspunkt
@main
struct RNV_Transport_AppApp: App {
    @StateObject private var graphQLService = GraphQLService()
    @StateObject private var locationManager = LocationManager()
    @StateObject private var liveActivityManager = LiveActivityManager()

    var body: some Scene {
        WindowGroup {
            ContentView()
                .environmentObject(graphQLService)
                .environmentObject(locationManager)
                .environmentObject(liveActivityManager)
        }
    }
}

Views subscriben mit @EnvironmentObject oder @ObservedObject und werden bei Änderungen automatisch neu gerendert.


6. Datenpersistenz & App Groups

Alle drei Targets teilen eine App Group für gemeinsame Datenspeicherung:

App Group ID: group.com.stefanfriedrich.rnvapp
Schlüssel Typ Beschreibung Zugriff
lastStations [Station] Zuletzt genutzte Haltestellen iOS App
activeTripData TripLiveActivityAttributes Aktive Fahrt iOS + Live Activity
savedTrips [DetailedTrip] Gespeicherte Verbindungen iOS + Watch
settings AppSettings Filter, Radius iOS App

7. Sicherheit

  • Credentials (Client ID + Secret) werden verschlüsselt gespeichert (EncryptionService, SecureConfigurationManager)
  • Bearer Tokens werden nur in-memory gehalten, nie persistiert
  • Standortdaten verlassen das Gerät nicht – nur der berechnete Suchradius wird als API-Parameter übermittelt
  • Keine Analytics, keine Drittanbieter-SDKs, kein Crash-Reporter

8. Swift Packages

Package Version Verwendung
Apollo iOS 2.0.4 Typsicherer GraphQL-Client

Alle anderen Abhängigkeiten sind Apple System-Frameworks: SwiftUI · ActivityKit · WidgetKit · CoreLocation · Combine · WatchConnectivity · BackgroundTasks

Clone this wiki locally