Initial commit for first version of project
This commit is contained in:
71
GasTankAR/Services/DocuframeService.swift
Normal file
71
GasTankAR/Services/DocuframeService.swift
Normal 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
|
||||
}
|
||||
44
GasTankAR/Services/StubDocuframeService.swift
Normal file
44
GasTankAR/Services/StubDocuframeService.swift
Normal file
@ -0,0 +1,44 @@
|
||||
//
|
||||
// StubDocuframeService.swift
|
||||
// GasTankAR
|
||||
//
|
||||
// Created by Dennis Nemec on 28.08.26.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import OSLog
|
||||
|
||||
/// Platzhalter-Implementierung des ``DocuframeService``. Simuliert Latenz und protokolliert
|
||||
/// die Aufrufe, führt aber noch keinen echten Upload/Mailversand aus. Die konkrete
|
||||
/// DOCUframe-Anbindung ersetzt diesen Stub später, ohne dass die Präsentationsschicht sich ändert.
|
||||
struct StubDocuframeService: DocuframeService {
|
||||
private static let logger = Logger(subsystem: "GasTankAR", category: "DOCUframe")
|
||||
|
||||
/// Optionaler HTTP-Statuscode, den der Stub simuliert. Bei 4xx/5xx wirft er den
|
||||
/// entsprechenden ``DocuframeError`` – so lässt sich die Fehlerbehandlung durchspielen.
|
||||
/// Standard `nil` = erfolgreicher Vorgang.
|
||||
private let simulatedStatusCode: Int?
|
||||
|
||||
init(simulatedStatusCode: Int? = nil) {
|
||||
self.simulatedStatusCode = simulatedStatusCode
|
||||
}
|
||||
|
||||
func uploadPhotos(_ photos: [DocuframePhoto], projectLeadID: String) async throws {
|
||||
try await simulate(operation: "Upload", photoCount: photos.count, projectLeadID: projectLeadID)
|
||||
}
|
||||
|
||||
func sendEmail(_ photos: [DocuframePhoto], projectLeadID: String) async throws {
|
||||
try await simulate(operation: "Mailversand", photoCount: photos.count, projectLeadID: projectLeadID)
|
||||
}
|
||||
|
||||
private func simulate(operation: String, photoCount: Int, projectLeadID: String) async throws {
|
||||
Self.logger.info("Stub-\(operation, privacy: .public) von \(photoCount) Foto(s) für Lead \(projectLeadID, privacy: .public)")
|
||||
try await Task.sleep(for: .seconds(1))
|
||||
|
||||
// Simuliert eine Server-Antwort: Bei 4xx/5xx wird der passende Fehler geworfen.
|
||||
if let simulatedStatusCode, let error = DocuframeError.from(statusCode: simulatedStatusCode) {
|
||||
Self.logger.error("DOCUframe-\(operation, privacy: .public) fehlgeschlagen (HTTP \(simulatedStatusCode))")
|
||||
throw error
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user