← Zurück zur Startseite

Projektdokumentation – SAMO Infra Tracker

Version: 1.1  |  Stand: Mai 2026  |  Plattform: iOS Swift / SwiftUI MapKit REST API


1. Projektüberblick

SAMO Infra Tracker ist eine native iOS-Anwendung zur GPS-basierten Erfassung, Verwaltung und Dokumentation von Infrastrukturelementen – insbesondere Gasleitungen. Die App dient als mobiler Client für das SAMO Infrastruktur-System (Asseco-BERIT) und richtet sich an professionelle Nutzer wie Netzbetreiber, Versorgungsunternehmen, Bauunternehmen, Wartungs- und Inspektionsdienste sowie Kommunen.

Kernfunktionen


2. Technologie-Stack

KomponenteTechnologieDetails
SpracheSwiftModerne, typsichere Programmiersprache von Apple
UI-FrameworkSwiftUIDeklaratives UI-Framework mit reaktiver Datenbindung
KartenMapKit (UIKit)Über UIViewRepresentable in SwiftUI eingebettet
GPS / LocationCoreLocationCLLocationManager mit Hintergrund-Tracking
NetzwerkURLSessionNative HTTP-Kommunikation mit async/await & Completion Handlers
LoggingOSLogApple-natives System für strukturiertes App-Logging
PersistenzJSON-Dateiconfig.json im Documents-Verzeichnis
LokalisierungLocalizable.xcstringsUnterstützung für Deutsch, Englisch, Französisch, Italienisch, Tschechisch und Japanisch
ArchitekturMVVMModel-View-ViewModel mit @ObservableObject
Min. iOS-VersioniOS 14.0+Unterstützt iPhone und iPad
BackendSAMO REST APIGIS-Server von Asseco-BERIT

2.1 Änderungen in Version 1.1 (Mai 2026)

Neue Features

Verbesserungen


3. Architektur (MVVM)

Die App folgt dem MVVM-Muster (Model-View-ViewModel) mit einem zentralen ViewModel als ObservableObject. Das ViewModel orchestriert die gesamte App-Logik, hält den State und koordiniert die Netzwerk-Schicht über den GisFeatureController.

3.1 Datenfluss

SAMO REST API ⟷ GisFeatureController ⟷ ViewModel (@Published) ⟷ Views (SwiftUI @Binding)

3.2 Architektur-Diagramm

┌─────────────────────────────────────────────────────────────────┐ │ SAMO REST API │ │ (Features, Tracks, Schäden, Auth, Logout) │ └──────────────────────────┬──────────────────────────────────────┘ │ HTTP (GET/PUT/POST) ▼ ┌──────────────────────────────────────────────────────────────────┐ │ GisFeatureController │ │ ┌────────────────┐ ┌──────────────┐ ┌────────────────────────┐ │ │ │ connect() │ │ loadFeatures │ │ uploadTrack() │ │ │ │ getFeatureCount│ │ (bbox) │ │ uploadSchaden() │ │ │ │ retryRequest() │ │ │ │ updateCurrentTrack() │ │ │ └────────────────┘ └──────────────┘ └────────────────────────┘ │ └──────────────────────────┬───────────────────────────────────────┘ │ [GisFeature], Responses ▼ ┌──────────────────────────────────────────────────────────────────┐ │ ViewModel (@ObservableObject) │ │ │ │ @Published leitungen @Published userLocation │ │ @Published myGpsPath @Published allSchaden │ │ @Published infobox @Published selectedLeitungIndex │ │ @Published followGPS @Published trackVerfolgen... │ │ │ │ CLLocationManagerDelegate ── GPS-Koordinaten empfangen │ │ wgs84ToLV95() ── Koordinatenumrechnung │ │ saveConfigToFile() ── Konfiguration persistieren │ └──────────────────────────┬───────────────────────────────────────┘ │ @Binding / @ObservedObject ▼ ┌──────────────────────────────────────────────────────────────────┐ │ Views (SwiftUI) │ │ ┌────────────────────┐ ┌───────────────┐ ┌────────────────┐ │ │ │ CustomContentView │ │ CustomMapView │ │ configPage │ │ │ │ (Hauptbildschirm) │ │ (MKMapView) │ │ (Einstellungen)│ │ │ └────────────────────┘ └───────────────┘ └────────────────┘ │ └──────────────────────────────────────────────────────────────────┘

4. Projektstruktur

SAMO Infra Tracker/ ├── SAMO_Infra_TrackerApp.swift ← App Entry Point (@main) ├── Assets.xcassets/ ← App-Icons, Farben, Bilder ├── model/ │ ├── GisFeatureController.swift ← Netzwerk-Schicht (REST API) │ └── ViewModel.swift ← Zentrales ViewModel (State, GPS, Logik) ├── structs/ │ ├── ConfigData.swift ← Konfigurationsdaten & Persistenz │ ├── configPage.swift ← Einstellungs-View │ ├── CrosshairOverlay.swift ← Fadenkreuz-Overlay für Karte │ ├── GisFeature.swift ← GIS-Feature-Datenmodell (Decodable) │ ├── GpsTrack.swift ← GPS-Track-Datenmodell │ ├── Leitungen.swift ← Leitungs-Datenmodell │ └── Schaden.swift ← Schadensmodell └── views/ ├── CustomContentView.swift ← Hauptbildschirm (Root View) └── customMapView.swift ← MapKit-Integration (UIViewRepresentable) SAMO Infra Tracker.xcodeproj/ ← Xcode-Projekt-Konfiguration SAMO-Infra-Tracker-Info.plist ← Background-Location-Modus Benutzeranleitung SAMO Infra Tracker.md ← Benutzeranleitung (Markdown) webpage/ ← Webseite & Dokumentation

5. Dateiübersicht

5.1 App Entry Point – SAMO_Infra_TrackerApp.swift

Einstiegspunkt der App, markiert mit @main. Erstellt ein WindowGroup mit CustomContentView als Root-View. Das ViewModel wird innerhalb von CustomContentView instanziiert.

@main
struct SAMO_Infra_TrackerApp: App {
    var body: some Scene {
        WindowGroup {
            CustomContentView(myPolylinesSelected: [])
        }
    }
}

5.2 Model / Netzwerk-Schicht – GisFeatureController.swift

Zentrale Klasse für die HTTP-Kommunikation mit dem SAMO REST API. Verwaltet Authentifizierung (Cookie-basiert mit Basic-Auth-Fallback), Feature-Downloads, Track-Uploads und Schadensmeldungen.

Wichtige Eigenschaften

EigenschaftTypBeschreibung
cookeStringSession-Cookie für authentifizierte Anfragen
bs64LoginTokenStringBase64-codierter Login-Token (Basic Auth)
features[GisFeature]Heruntergeladene GIS-Features
baseUrlStringREST-Basis-URL
maxRetriesIntMax. Wiederholungsversuche (10)
retryDelayTimeIntervalWartezeit zwischen Retries (2 Sek.)

Wichtige Methoden

MethodeBeschreibung
connect()Einstiegspunkt: Feature-Anzahl abfragen, dann Features laden
getFeatureCount(type:useCookie:)GET /{type}/count → Gesamtzahl der Features
loadFeatures(type:url:...boundingBox:append:)GET /{type}?srs=epsg:4326&size=…&bbox=… → Features im Kartenausschnitt (append: Features hinzufügen oder ersetzen)
uploadTrack(idLeitung:trackFeature:)PUT → GPS-Track erstellen und mit Leitung verknüpfen
uploadTrackOhneLeitung(trackFeature:)PUT → GPS-Track ohne Leitungsverknüpfung erstellen (neu in v1.1)
updateCurrentTrackFeature(trackFeature:)POST → Laufenden Track aktualisieren
uploadSchaden(schadenFeature:)PUT → Schadensmeldung mit optionalen Foto-Attachments erstellen und mit Track verknüpfen
retryRequest(request:)Generischer Retry-Wrapper (bis 10 Versuche, 2s Delay bei 401)
extractAndSetCookie(from:)Session-Cookie aus Response-Headers extrahieren

5.3 ViewModel – ViewModel.swift

Zentrales ObservableObject, das den gesamten App-State verwaltet. Erbt von NSObject und implementiert CLLocationManagerDelegate für GPS-Updates. Orchestriert den GisFeatureController und stellt @Published-Properties für die Views bereit.

Zentrale @Published Properties

PropertyTypZweck
myGpsPathGpsPathAktuell aufgezeichneter GPS-Track
allSchaden[Schaden]Alle erfassten Schadensmeldungen
leitungen[Leitungen]Alle heruntergeladenen Leitungen
selectedLeitungIndexIntIndex der ausgewählten Leitung (-1 = keine)
userLocationCLLocationCoordinate2D?Aktuelle GPS-Position
followGPSBoolKarte folgt automatisch der Position
trackVerfolgenUndAufzeichnenBoolGPS-Aufzeichnung aktiv
infoboxStringStatusmeldungen in der UI
uriString, username, passwordStringServer-Verbindungsdaten
lineFeatureTypeStringSAMO-Feature-Typ für Leitungen
pointFeatureTypeStringSAMO-Feature-Typ für Schäden
gpsTrackFeatureTypeStringSAMO-Feature-Typ für GPS-Tracks

Wichtige Methoden

MethodeBeschreibung
init()Lädt Konfiguration aus JSON, initialisiert GisFeatureController, startet Verbindung
startUpdatingLocation()GPS-Tracking starten: 2s-Timer, Track hochladen, Features im Sichtbereich laden
stopUpdatingLocation()GPS-Tracking beenden
downloadAllFeaturesInBoundingBox()Features für den aktuellen Kartenausschnitt neu laden
createAllGasLeitungen()Konvertiert [GisFeature] → [Leitungen], filtert Niederdruck
selectGasleitung(_:)Leitung auswählen / abwählen
neuenSchadenErstellen(Text:)Schaden an aktueller Position erstellen und hochladen
neuerTrack()Aktuellen Track archivieren, neuen starten
gpsPathVerknuepfen()GPS-Track mit ausgewählter Leitung verknüpfen
wgs84ToLV95(lat:lon:)WGS84 → Swiss LV95 Koordinatenumrechnung
checkServerConnection()Verbindungstest: GET /rest/security/getCurrentUser
saveConfigToFile() / loadConfigFromFile()Konfiguration als JSON speichern / laden

5.4 Datenstrukturen (Structs)

Alle Datenmodelle befinden sich im Ordner structs/. Details zu den einzelnen Structs finden Sie in Abschnitt 6. Datenmodelle.

5.5 Views

Details zur Benutzeroberfläche finden Sie in Abschnitt 8. Benutzeroberfläche.


6. Datenmodelle

6.1 GisFeature (GisFeature.swift)

Decodable-Modell für die JSON-Antworten des SAMO REST API. Repräsentiert ein geographisches Feature (Leitung).

StructBeschreibung
GisFeatureHauptmodell: id, sid, Geometrien, Attribute. Implementiert Identifiable und Decodable.
geometryGeometrie-Typ (LineString) mit coordinates: [[Double]]
geometriesEpsg4326Wrapper für Geometrie im EPSG:4326 Koordinatensystem
at_30001_02Attribut-Struct mit ca_GA_DRUCK_02 (Druckzone, z.B. "Niederdruck")

Methoden

6.2 Leitungen (Leitungen.swift)

Repräsentiert eine Leitung (z.B. Gasleitung) für die Kartenanzeige. Konvertiert aus GisFeature.

EigenschaftTypBeschreibung
titleStringAnzeigename der Leitung
idStringEindeutige SAMO-ID
coordinates[CLLocationCoordinate2D]Koordinaten des Leitungsverlaufs
druckzoneString?Druckzone (Niederdruck wird gefiltert)
myPolyLineMKPolyline?Vorberechnete Polyline für MapKit

Extension: isNear(coordinate:) – Hit-Test, ob eine Koordinate innerhalb von 15m liegt (für Tap-Erkennung).

6.3 GpsPath (GpsTrack.swift)

Repräsentiert einen aufgezeichneten GPS-Track (Pfad aus Koordinaten).

Eigenschaft / MethodeBeschreibung
coordinatesArray aller aufgezeichneten GPS-Punkte
idServer-ID nach Upload (initial nil)
addCoordinate(_:_:)Neuen GPS-Punkt hinzufügen
getDistance()Gesamtlänge des Tracks in Metern
getPolyLine()MKPolyline für Kartenrendering
myEncode(viewModel:)JSON-Encoding mit LV95-konvertierten Koordinaten
myEncode(viewModel:beschreibung:)Vollständiges JSON-Encoding mit Beschreibung und Attributen
Hinweis: Koordinaten werden vor dem Upload automatisch von WGS84 nach LV95 umgerechnet (über viewModel.wgs84ToLV95()).

6.4 Schaden (Schaden.swift)

Repräsentiert eine Schadensmeldung an einer Punktposition.

EigenschaftTypBeschreibung
beschreibungStringBeschreibungstext des Schadens
coordinates[Double]Swiss LV95 Koordinaten [E, N]
wgs84CoordinatesCLLocationCoordinate2DWGS84-Koordinaten für Kartenanzeige
connectToIdStringID des zugehörigen GPS-Tracks

myEncode() erzeugt das JSON für die SAMO API mit Attributen wie at_alg_report_description und Punkt-Geometrie.

6.5 ConfigData (ConfigData.swift)

Codable-Struct für die Persistierung aller Einstellungen.

FelderBeschreibung
uriString, username, passwordServer-Verbindungsdaten
lineFeatureType, pointFeatureType, gpsTrackFeatureTypeSAMO Feature-Typen
Relationen & RollenKonfigurierbare Beziehungstypen
cookieGespeicherter Session-Cookie
polylineName, pointNameAnzeigenamen (z.B. "Gasleitung", "Schaden")

ConfigManager (Singleton): save(_:) und load() lesen/schreiben config.json im Documents-Verzeichnis.


7. Netzwerk & API

7.1 Authentifizierung

Die App verwendet eine zweistufige Authentifizierungsstrategie:

  1. Cookie-basiert (Primär): Bei vorhandenem Session-Cookie wird dieser für Anfragen verwendet. Der Cookie wird aus Response-Headers extrahiert und persistiert.
  2. Basic Auth (Fallback): Falls kein Cookie vorhanden ist oder der Cookie abgelaufen ist, wird ein Base64-codierter username:password-Token als Authorization: Basic …-Header gesendet.

Bei 401 Unauthorized-Antworten wechselt die App automatisch zurück zu Basic Auth und versucht einen neuen Cookie zu erhalten.

7.2 API-Endpunkte

EndpunktMethodeZweck
{base}/rest/features/{type}/count GET Anzahl der Features eines Typs abfragen
{base}/rest/features/{type}?srs=epsg:4326&size=…&bbox=… GET Features innerhalb einer Bounding Box laden
{base}/rest/features/{lineType}/{id}/relations/{trackType}/{relation}/{role}/create PUT GPS-Track erstellen und mit Leitung verknüpfen
{base}/rest/features/{trackType} PUT GPS-Track ohne Leitungsverknüpfung erstellen (neu in v1.1)
{base}/rest/features/{trackType}/{trackId} POST Bestehenden GPS-Track aktualisieren (Geometrie erweitern)
{base}/rest/features/{trackType}/{trackId}/relations/{pointType}/{relation}/{role}/create PUT Schadensmeldung mit optionalen Foto-Attachments erstellen und mit Track verknüpfen
{base}/rest/security/getCurrentUser GET Verbindung prüfen / aktuellen Benutzer abrufen
{base}/api/rest/management/user/logout GET Session beenden / Logout
Basis-URL-Format: https://samo.example.com/EZL – Die REST-Pfade werden automatisch angehängt.

7.3 Retry-Logik

Alle Netzwerkanfragen nutzen eine generische Retry-Logik (retryRequest()):


8. Benutzeroberfläche

8.1 Hauptbildschirm – CustomContentView.swift

Root-View der App mit folgendem Aufbau (von oben nach unten):

┌──────────────────────────────────────────────┐ │ ⚙️ Konfig │ 🌐 GPS │ 🧹 Reset │ 📍 Track │ ← Toolbar (HScrollView) ├──────────────────────────────────────────────┤ │ ☑ Position automatisch aktualisieren │ ← Auto-Follow Toggle ├──────────────────────────────────────────────┤ │ │ │ CustomMapView (MapKit) │ ← Interaktive Karte │ (dynamisch skalierbar) │ (200–550pt Höhe) │ │ ├────────────────── ▬▬▬ ──────────────────────┤ ← Drag-Handle │ 📍 Swiss-Koordinaten der Schadensstelle │ ├──────────────────────────────────────────────┤ │ Gasleitung 1 │ Gasleitung 2 │ ... │ ← Leitungsliste ├──────────────────────────────────────────────┤ │ [ Beschreibung eingeben ] [Schaden erfassen]│ ← Schadenseingabe ├──────────────────────────────────────────────┤ │ Infobox: Statusmeldungen │ ← Infobox └──────────────────────────────────────────────┘

Verhalten

8.2 Kartenansicht – customMapView.swift

UIViewRepresentable-Wrapper um MKMapView für volle MapKit-Kontrolle. Ein Coordinator dient als MKMapViewDelegate.

Overlay-Farbkodierung

FarbeElementStil
OrangeAktueller GPS-TrackGestrichelt, 3pt
GrauArchivierte TracksGestrichelt
BlauAusgewählte LeitungSolide, 4pt
GrünZusätzlich selektierte LeitungenGestrichelt
RotStandard-LeitungenSolide, 2pt
Rot (Kreuz)Crosshair-Overlay (Schadenspositionen)Fadenkreuz
Blau (Kreis)GPS-Position des BenutzersKreisförmig

Interaktionen

8.3 Konfigurationsseite – configPage.swift

Einstellungs-View mit @Binding-Properties zum ViewModel. Zwei Formularbereiche:

Drei Aktionsbuttons: Konfiguration speichern, Verbindung prüfen, Verbindung trennen.


9. Koordinatensysteme

Die App arbeitet mit zwei Koordinatensystemen und konvertiert automatisch zwischen diesen:

SystemEPSG-CodeVerwendung
WGS84EPSG:4326GPS-Empfang (CoreLocation), Kartenanzeige (MapKit), Feature-Download
Swiss LV95EPSG:2056Upload zu SAMO API, Bounding-Box-Berechnung, Schweizer Koordinatenanzeige

Umrechnung WGS84 → LV95

Die Methode wgs84ToLV95(lat:lon:) im ViewModel implementiert die offiziellen swisstopo-Formeln zur Umrechnung von WGS84-Breitengrad/Längengrad in Schweizer LV95-Koordinaten (Easting/Northing).

Bounding Box

boundingBoxLV95FromGPS(lat:lon:edgeLengthMeters:) berechnet eine quadratische Bounding Box in LV95-Koordinaten um eine GPS-Position. Diese wird für den Feature-Download verwendet, um nur Features im aktuellen Kartenausschnitt zu laden.


10. Konfiguration & Persistenz

Alle Einstellungen werden als JSON-Datei im Documents-Verzeichnis der App gespeichert:

Documents/config.json

Gespeicherte Daten

Speicher-Zeitpunkte

ConfigManager (Singleton)

MethodeBeschreibung
save(_:)Encodiert ConfigData nach JSON und schreibt in die Datei
load()Liest und decodiert ConfigData aus der Datei

11. Zentrale Workflows

11.1 App-Start

App starten │ ▼ ViewModel.init() │ ├── loadConfigFromFile() → config.json lesen │ ├── GisFeatureController erstellen │ └── connect() │ ├── getFeatureCount() → GET /{type}/count │ └── loadFeatures() → GET /{type}?bbox=… │ └── createAllGasLeitungen() → [Leitungen] für Karte

11.2 GPS-Tracking

User tippt "GPS einschalten" │ ▼ startUpdatingLocation() │ ├── CLLocationManager.startUpdatingLocation() │ ├── uploadTrack() → PUT (neuen Track mit Leitung verknüpfen) │ │ │ └── Track-ID empfangen │ └── Timer (alle 2 Sekunden) │ ├── GPS-Koordinate zu myGpsPath hinzufügen │ ├── updateCurrentTrackFeature() → POST (Track-Geometrie aktualisieren) │ └── downloadAllFeaturesInBoundingBox() → Features für Sichtbereich

11.3 Schadensmeldung

User tippt "Schaden erfassen" │ ▼ neuenSchadenErstellen(Text:) │ ├── Aktuelle GPS-Position → wgs84ToLV95() → LV95-Koordinaten │ ├── Schaden-Objekt erstellen (Beschreibung, Koordinaten, Track-ID) │ ├── uploadSchaden() → PUT (Schaden mit Track verknüpfen) │ └── allSchaden[] aktualisieren → Karte zeigt Annotation

11.4 Karteninteraktion

User bewegt / zoomt Karte │ ▼ regionDidChangeAnimated (MKMapViewDelegate) │ ├── Neue Karten-Region → ViewModel aktualisieren │ ├── boundingBoxLV95FromGPS() → Neue Bounding Box berechnen │ └── downloadAllFeaturesInBoundingBox() │ └── loadFeatures(bbox:) → createAllGasLeitungen() → UI-Update

12. Build & Systemanforderungen

Entwicklungsumgebung

AnforderungWert
IDEXcode (aktuell)
SpracheSwift
ProjekttypXcode Project (.xcodeproj)
AbhängigkeitenKeine externen Pakete – nur Apple Frameworks

Verwendete Frameworks

FrameworkZweck
SwiftUIDeklaratives UI
MapKitKartenanzeige, Overlays, Annotations
CoreLocationGPS-Tracking, Standortbestimmung
FoundationURLSession, JSON-Encoding/-Decoding, FileManager

Zielplattform

EigenschaftWert
PlattformiOS (iPhone + iPad)
Min. iOS-Version14.0
Benötigte BerechtigungenStandort ("Immer"), Netzwerk
Background Modeslocation (in Info.plist konfiguriert)
Speicherbedarfca. 50 MB

Build-Schritte

  1. Repository / Projektordner in Xcode öffnen (SAMO Infra Tracker.xcodeproj)
  2. Signing & Capabilities konfigurieren (Apple Developer Account erforderlich)
  3. Zielgerät oder Simulator auswählen
  4. ⌘ + B zum Bauen, ⌘ + R zum Ausführen
Wichtig: Für GPS-Tracking auf dem Simulator stehen nur simulierte Standorte zur Verfügung. Für vollständige Tests ist ein physisches iOS-Gerät empfohlen.

13. Glossar

BegriffBeschreibung
SAMOGIS-Infrastruktur-System von Asseco-BERIT für die Verwaltung von leitungsgebundener Infrastruktur
GISGeographisches Informationssystem – System zur Erfassung, Verwaltung und Analyse räumlicher Daten
FeatureEin geographisches Objekt im GIS (z.B. eine Leitung, ein Schaden, ein GPS-Track)
Feature-TypKlassifizierung eines Features (z.B. ft_30015 für Gasleitungen)
RelationVerknüpfung zwischen zwei Features (z.B. Track ↔ Leitung)
RoleRolle eines Features in einer Relation (z.B. rt_tracking)
WGS84World Geodetic System 1984 – globales Koordinatensystem (GPS-Standard), EPSG:4326
Swiss LV95Schweizer Landesvermessung 1995 – nationales Koordinatensystem der Schweiz, EPSG:2056
Bounding BoxRechteckiger Kartenausschnitt (min/max Koordinaten) für den Feature-Download
EPSGEuropean Petroleum Survey Group – Registrierung von Koordinatenreferenzsystemen
PolylineLinienzug aus Koordinaten – Darstellung von Leitungen und Tracks auf der Karte
AnnotationKartenmarkierung (Pin) an einer Punktposition (z.B. für Schäden)
OverlayGrafische Ebene über der Karte (Polylines, Crosshairs, Kreise)
MVVMModel-View-ViewModel – Architekturmuster zur Trennung von Daten, Logik und UI
DruckzoneKategorie einer Gasleitung nach Druck (z.B. Niederdruck, Mitteldruck, Hochdruck)
CrosshairFadenkreuz-Overlay zur Markierung exakter Positionen auf der Karte
TrackAufgezeichneter GPS-Pfad (Folge von Koordinaten)
Track ohne LeitungGPS-Track, der unabhängig von einer Infrastrukturleitung erstellt wird (neu in v1.1)
Foto-AttachmentFotografische Dokumentation, die einer Schadensmeldung beigefügt wird (neu in v1.1)
OSLogApple-natives Logging-Framework für strukturiertes, effizienten App-Logging (neu in v1.1)

Projektdokumentation SAMO Infra Tracker  |  Stand: Mai 2026
← Zurück zur Startseite  |  Zur Benutzeranleitung