ScanCarteViewModel.swift
119 lignes · 3800 octets
import Foundation import UIKit // Port of ui/ScanCarteViewModel.kt. The three Android pipeline stages // (Scanning/OcrRunning/Structuring) collapse into one `.processing` state: // `ScanPipeline.process` runs the whole chain in a single call. enum ScanUiState { case idle case processing case draftReady(ContactDraftUi) case error(String) } struct ContactDraftUi { var card: ContactCard var rawOcrText: String var preview: UIImage? } @MainActor final class ScanCarteViewModel: ObservableObject { @Published private(set) var state: ScanUiState = .idle @Published private(set) var saveDoneId: Int64? /// Non-nil when rescanning an existing contact (update path). var editingContactId: Int64? var isEditing: Bool { editingContactId != nil } private let pipeline: ScanPipeline private let repository: ContactRepository init( pipeline: ScanPipeline = ScanPipeline(), repository: ContactRepository = ContactRepository( dao: Card2vcfDatabase.shared.crmContactDao, images: ContactImageStore() ) ) { self.pipeline = pipeline self.repository = repository } func processCapture(_ image: UIImage, source: ScanSource) { state = .processing Task { do { let output = try await pipeline.process(image, source: source) var card = output.draft // Rescan keeps the existing notes when the draft has none. if let editId = editingContactId, let existing = try await repository.getById(editId), let notes = existing.notes?.nilIfBlank { card.note = notes } state = .draftReady( ContactDraftUi(card: card, rawOcrText: output.ocrText, preview: output.preview) ) } catch let e as ScanException { state = .error(e.message) } catch let e as OcrException { state = .error(e.message) } catch { state = .error(error.localizedDescription.nilIfBlank ?? "Capture impossible — réessayez") } } } func updateDraft(_ card: ContactCard) { if case .draftReady(var draft) = state { draft.card = card state = .draftReady(draft) } } /// Saves the draft into the local carnet (new contact), or updates the /// rescanned contact. Emits the id via `saveDoneId`. func saveToCarnet() { guard case .draftReady(let draft) = state else { return } if let editId = editingContactId { Task { do { let path = draft.preview.flatMap { DraftImageWriter.writeJpeg($0, prefix: "card") } try await repository.updateFromCard( id: editId, card: draft.card, cardImagePath: path ) saveDoneId = editId } catch { state = .error(error.localizedDescription) } } return } Task { do { let path = draft.preview.flatMap { DraftImageWriter.writeJpeg($0, prefix: "card") } let id = try await repository.insertFromCard(draft.card, cardImagePath: path) saveDoneId = id } catch { state = .error(error.localizedDescription) } } } func consumeSaveDone() { saveDoneId = nil } func reset() { state = .idle } func onCaptureDecodeFailed() { state = .error("Capture impossible — réessayez") } }
GitRust