banyaro-ios/BanYaroGo/Support/GPXExporter.swift
rene c01e3d6be7 Phase 3.6: B+C+D komplett + HealthKit Sync
D.10 401-Handling: APIError.unauthorized, NotificationCenter-Bridge,
  AuthSession.logout() bei 401 → User landet wieder im Login

D.12 PWA-Deep-Links: Settings-Section mit Forum/Hunde/Walks/Settings
  öffnet Safari per https://banyaro.app/#fragment

B.4 Auto-Pause: 2-min-Inaktivität → isAutoPaused, automatischer Resume bei
  nächstem GPS-Update. Settings-Toggle, im UI eigenes Badge "Auto-Pause"
  (grau vs. Pause orange).

C.7 Edit/Delete: RouteUpdateBody + APIClient.patch + APIClient.delete,
  EditRouteSheet (Name/Beschreibung/Public), Menu in Toolbar (nur eigene
  Touren), Alert für Delete.

C.9 Statistik-Tab: neuer Tab "Statistik" zwischen Hunde und Mehr. Filtert
  /api/routes auf meine Touren, rechnet Woche/Monat/Allzeit (Distanz, Dauer,
  Touren), Längste Tour, aktuelle Streak (Tage in Folge).

B.5 Walk-Review: Map-Header an die Spitze des FinishWalkSheet-Forms.

B.6 Geo-Fotos: CapturedPhoto (Data + GPSPoint?), PhotoLocation @Model in
  SwiftData. Kamera während Walk taggt mit tracker.points.last. Nach Upload:
  foto_url aus Response → PhotoLocation persistiert. MiniRouteMap rendert
  Annotations mit Tap-Callback, PhotoViewerSheet zeigt Foto fullscreen.

C.8 Share PNG+GPX: RouteShareImage (MKMapSnapshotter + Polyline overlay +
  SwiftUI ShareCard via ImageRenderer), GPXExporter (Tempfile mit XML),
  ShareSheet (UIActivityViewController-Wrapper), Menu in Route-Toolbar.

D.11 Icon-Varianten: AppIcon-Dark (0.45 Brightness), AppIcon-Tinted
  (Grayscale + Kontrastverstärkung), Contents.json mit appearance entries.

A.2 HealthKit: BanYaroGo.entitlements (com.apple.developer.healthkit),
  NSHealthShare/UpdateUsageDescription. WalkHealthSync.shared mit
  HKWorkoutBuilder (.walking) + HKWorkoutRouteBuilder, Timestamps gleichmäßig
  über Walk-Dauer verteilt. Settings-Toggle mit Permission-Request.
2026-05-30 11:19:53 +02:00

47 lines
1.6 KiB
Swift

import Foundation
enum GPXExporter {
/// Writes the route to a temporary `.gpx` file and returns its URL.
static func write(detail: RouteDetail) -> URL? {
let xml = generate(detail: detail)
let safeName = detail.name
.replacingOccurrences(of: "/", with: "-")
.trimmingCharacters(in: .whitespacesAndNewlines)
let filename = safeName.isEmpty ? "tour" : safeName
let url = FileManager.default.temporaryDirectory
.appendingPathComponent("\(filename).gpx")
do {
try xml.write(to: url, atomically: true, encoding: .utf8)
return url
} catch {
return nil
}
}
static func generate(detail: RouteDetail) -> String {
let trkpts = detail.gpsTrack.map { p -> String in
if let alt = p.alt {
return " <trkpt lat=\"\(p.lat)\" lon=\"\(p.lon)\"><ele>\(alt)</ele></trkpt>"
} else {
return " <trkpt lat=\"\(p.lat)\" lon=\"\(p.lon)\"/>"
}
}.joined(separator: "\n")
let safeName = detail.name
.replacingOccurrences(of: "&", with: "&amp;")
.replacingOccurrences(of: "<", with: "&lt;")
.replacingOccurrences(of: ">", with: "&gt;")
return """
<?xml version="1.0" encoding="UTF-8"?>
<gpx version="1.1" creator="Ban Yaro Go" xmlns="http://www.topografix.com/GPX/1/1">
<trk>
<name>\(safeName)</name>
<trkseg>
\(trkpts)
</trkseg>
</trk>
</gpx>
"""
}
}