Files
Gas-Tank-AR-App/GasTankAR/Views/TankAR/ARProjectPersisting.swift
Dennis Nemec b8536d4182 Add tank catalog with USDZ models and tank selection
Introduce TankDefinition/TankCatalog loaded from Tanks.json, bundle the
Drachengas USDZ tank models, add tank selection and preview views, and
update the AR experience, editor and project views to use the catalog.
Includes tests for catalog loading and model selection.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-26 19:12:39 +02:00

92 lines
2.8 KiB
Swift

//
// ARProjectPersisting.swift
// GasTankAR
//
// Created by Dennis Nemec on 26.08.26.
//
import ARKit
import CoreLocation
import SwiftData
/// Abstraktion der Projekt-Persistenz für den AR-Modus (Dependency Inversion):
/// Der Controller kennt so kein SwiftData und ist unit-testbar.
@MainActor
protocol ARProjectPersisting: AnyObject {
var tank: Tank { get }
var worldMap: ARWorldMap? { get }
var tankTransform: [Float]? { get }
var savedMeasurements: [Measurement] { get }
var savedPipeRoute: PipeRoute { get }
func saveWorldMap(_ data: Data)
func saveTankTransform(_ transform: [Float])
func saveMeasurements(_ measurements: [Measurement])
func savePipeRoute(_ route: PipeRoute)
func clearTankTransform()
func savePhoto(_ imageData: Data, date: Date, coordinate: CLLocationCoordinate2D?)
}
/// SwiftData-Adapter: verbindet das `Project`-Model mit dem `ARProjectPersisting`-Protokoll.
@MainActor
final class ProjectPersistenceAdapter: ARProjectPersisting {
private let project: Project
private let modelContext: ModelContext
init(project: Project, modelContext: ModelContext) {
self.project = project
self.modelContext = modelContext
}
var tank: Tank { project.tank }
var worldMap: ARWorldMap? { project.worldMap }
var tankTransform: [Float]? { project.tankTransform }
var savedMeasurements: [Measurement] {
guard let data = project.measurementsData else { return [] }
return (try? JSONDecoder().decode([Measurement].self, from: data)) ?? []
}
var savedPipeRoute: PipeRoute {
guard let data = project.pipeRouteData else { return PipeRoute() }
return (try? JSONDecoder().decode(PipeRoute.self, from: data)) ?? PipeRoute()
}
func saveWorldMap(_ data: Data) {
project.worldMapData = data
try? modelContext.save()
}
func saveTankTransform(_ transform: [Float]) {
project.tankTransform = transform
try? modelContext.save()
}
func saveMeasurements(_ measurements: [Measurement]) {
project.measurementsData = try? JSONEncoder().encode(measurements)
try? modelContext.save()
}
func savePipeRoute(_ route: PipeRoute) {
project.pipeRouteData = try? JSONEncoder().encode(route)
try? modelContext.save()
}
func clearTankTransform() {
project.tankTransform = nil
try? modelContext.save()
}
func savePhoto(_ imageData: Data, date: Date, coordinate: CLLocationCoordinate2D?) {
let photo = ProjectPhoto(
creationDate: date,
imageData: imageData,
latitude: coordinate?.latitude,
longitude: coordinate?.longitude,
project: project
)
modelContext.insert(photo)
try? modelContext.save()
}
}