-
Notifications
You must be signed in to change notification settings - Fork 0
Architektur
I767513 edited this page May 10, 2026
·
2 revisions
Die App folgt dem MVVM-Muster (Model – View – ViewModel) mit SwiftUI und ist in drei Build-Targets aufgeteilt.
- Targets & Verantwortlichkeiten
- Ordnerstruktur
- Schichtenarchitektur
- Datenfluss
- State Management
- Datenpersistenz & App Groups
- Sicherheit
- Swift Packages
┌─────────────────────────────────────────────────────────┐
│ 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 |
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)
┌─────────────────────────────────────┐
│ 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 │
└─────────────────────────────────────┘
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
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.
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 |
-
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
| 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
Home · Einrichtung · Architektur · RNV API · Live Activities · Apple Watch · FAQ · Beitragen
🚀 Einstieg
📐 Technik
📋 Sonstiges