Initial commit for first version of project

This commit is contained in:
Dennis Nemec
2026-09-02 20:54:36 +02:00
parent 3d9f1ab6bc
commit 992bb32025
56 changed files with 5139 additions and 31 deletions

View File

@ -0,0 +1,71 @@
//
// DocuframeService.swift
// GasTankAR
//
// Created by Dennis Nemec on 28.08.26.
//
import Foundation
/// Foto-Übergabeobjekt für DOCUframe-Vorgänge – bewusst unabhängig von der
/// Persistenzschicht (SwiftData), damit die Domäne keine Speicher-Details kennt.
struct DocuframePhoto: Sendable {
let imageData: Data
let creationDate: Date
let latitude: Double?
let longitude: Double?
}
/// Fehler bei DOCUframe-Vorgängen.
enum DocuframeError: LocalizedError, Equatable {
case noPhotosSelected
/// Der Server hat mit einem 4xx-Status geantwortet (fehlerhafte Anfrage).
case clientError(statusCode: Int)
/// Der Server hat mit einem 5xx-Status geantwortet (Serverproblem).
case serverError(statusCode: Int)
/// Netzwerk-/Transportfehler (z. B. keine Verbindung).
case transport(String)
var errorDescription: String? {
switch self {
case .noPhotosSelected:
"Es wurden keine Fotos ausgewählt."
case .clientError(let statusCode):
"Die Anfrage wurde von DOCUframe abgelehnt (Fehler \(statusCode)). Bitte prüfe die Daten und versuche es erneut."
case .serverError(let statusCode):
"DOCUframe ist derzeit nicht erreichbar (Serverfehler \(statusCode)). Bitte versuche es später erneut."
case .transport(let message):
message
}
}
/// Bildet einen HTTP-Statuscode auf den passenden Fehler ab – `nil` bei Erfolg (2xx/3xx).
static func from(statusCode: Int) -> DocuframeError? {
switch statusCode {
case 400..<500: .clientError(statusCode: statusCode)
case 500..<600: .serverError(statusCode: statusCode)
default: nil
}
}
}
extension HTTPURLResponse {
/// Wirft einen ``DocuframeError``, wenn der Statuscode einen 4xx-/5xx-Fehler darstellt.
func validateDocuframeStatus() throws {
if let error = DocuframeError.from(statusCode: statusCode) {
throw error
}
}
}
/// Abstraktion des DOCUframe-Zugriffs (Upload & Mailversand).
///
/// Clean-Architecture-Grenze: Die Präsentationsschicht hängt nur von diesem Protokoll ab,
/// nicht von der konkreten DOCUframe-Anbindung. Konkrete Implementierungen (Stub, echte
/// Anbindung, Mock für Tests) liegen in der Infrastruktur- bzw. Testschicht.
protocol DocuframeService: Sendable {
/// Lädt die übergebenen Fotos zum Projekt (Lead) in DOCUframe hoch.
func uploadPhotos(_ photos: [DocuframePhoto], projectLeadID: String) async throws
/// Versendet die übergebenen Fotos als E-Mail über DOCUframe.
func sendEmail(_ photos: [DocuframePhoto], projectLeadID: String) async throws
}