proofmode 0.9.0

Capture, share, and preserve verifiable photos and videos
Documentation
import SwiftUI
import Combine
import CoreLocation
import UIKit

@MainActor
class ProofViewModel: NSObject, ObservableObject {
    @Published var proofs: [Proof] = []
    @Published var isGenerating = false
    @Published var isVerifying = false
    @Published var currentStatus = ""
    @Published var showingError = false
    @Published var errorMessage = ""
    @Published var showingSuccess = false
    @Published var successMessage = ""

    private let proofModeService = ProofModeService()
    private let locationManager = CLLocationManager()
    private var currentLocation: CLLocation?

    override init() {
        super.init()
        setupLocationManager()
        loadProofs()
    }

    private func setupLocationManager() {
        locationManager.delegate = self
        locationManager.desiredAccuracy = kCLLocationAccuracyBest
    }

    func createProof(for image: UIImage) {
        guard let imageData = image.jpegData(compressionQuality: 0.8) else {
            showError("Failed to process image")
            return
        }

        isGenerating = true
        currentStatus = "Preparing image..."

        // Request location if enabled
        if UserDefaults.standard.bool(forKey: "includeLocation") {
            locationManager.requestWhenInUseAuthorization()
            locationManager.requestLocation()
        }

        // Create file name
        let fileName = "IMG_\(Date().timeIntervalSince1970).jpg"

        // Get metadata using UniFFI types
        let location = currentLocation.map { loc in
            LocationData(
                latitude: loc.coordinate.latitude,
                longitude: loc.coordinate.longitude,
                altitude: loc.altitude,
                accuracy: loc.horizontalAccuracy,
                provider: "CoreLocation"
            )
        }

        let deviceInfo = DeviceData(
            manufacturer: "Apple",
            model: UIDevice.current.model,
            osVersion: UIDevice.current.systemVersion,
            deviceId: UIDevice.current.identifierForVendor?.uuidString
        )

        let networkData = NetworkData(
            networkType: getNetworkType(),
            wifiSsid: nil,
            cellInfo: nil
        )

        currentStatus = "Generating proof..."

        // Calculate hash using ProofMode
        let hash = proofModeService.calculateHash(for: imageData)

        // Create metadata dictionary for ProofMode
        var metadata: [String: String] = [:]
        metadata["fileName"] = fileName
        metadata["timestamp"] = ISO8601DateFormatter().string(from: Date())
        metadata["mime_type"] = "image/jpeg"

        // Create config
        let config = ProofModeConfig(
            autoNotarize: false,
            trackLocation: UserDefaults.standard.bool(forKey: "includeLocation"),
            trackDeviceId: UserDefaults.standard.bool(forKey: "includeDeviceInfo"),
            trackNetwork: UserDefaults.standard.bool(forKey: "includeNetworkInfo"),
            addCredentials: false,
            embedC2pa: true
        )

        // Call ProofMode generate function on a background thread (synchronous API)
        DispatchQueue.global(qos: .userInitiated).async { [weak self] in
            guard let self = self else { return }

            do {
                let callbacks = ProofModeCallbacksHandler(service: self.proofModeService)
                let _ = try generateProof(
                    mediaData: imageData,
                    metadata: metadata,
                    config: config,
                    callbacks: callbacks
                )

                // Create proof object
                let proof = Proof(
                    fileName: fileName,
                    hash: hash,
                    fileSize: Int64(imageData.count),
                    isVerified: true,
                    location: location,
                    deviceInfo: deviceInfo,
                    networkInfo: networkData,
                    signature: "generated",
                    signedBy: "ProofMode iOS Example",
                    imageData: imageData
                )

                DispatchQueue.main.async {
                    // Save proof
                    self.proofs.insert(proof, at: 0)
                    self.saveProofs()

                    self.isGenerating = false
                    self.currentStatus = ""
                    self.successMessage = "Proof generated successfully!"
                    self.showingSuccess = true
                }
            } catch {
                DispatchQueue.main.async {
                    self.isGenerating = false
                    self.currentStatus = ""
                    self.showError("Failed to generate proof: \(error.localizedDescription)")
                }
            }
        }
    }

    func verifyProof(_ proof: Proof) async -> Bool {
        isVerifying = true

        guard let imageData = proof.imageData else {
            isVerifying = false
            return false
        }

        let calculatedHash = proofModeService.calculateHash(for: imageData)
        let result = calculatedHash == proof.hash

        isVerifying = false
        return result
    }

    func deleteProof(_ proof: Proof) {
        proofs.removeAll { $0.id == proof.id }
        saveProofs()
    }

    func clearAllProofs() {
        proofs.removeAll()
        saveProofs()
    }

    func refreshProofs() async {
        // In a real app, this might sync with a server
        // For now, just reload from storage
        loadProofs()
    }

    private func loadProofs() {
        // Persistence disabled temporarily since UniFFI types don't conform to Codable
        // In a real app, implement custom serialization or use a different storage approach
        proofs = []
    }

    private func saveProofs() {
        // Persistence disabled temporarily since UniFFI types don't conform to Codable
        // In a real app, implement custom serialization or use a different storage approach
    }

    private func showError(_ message: String) {
        errorMessage = message
        showingError = true
    }

    private func getNetworkType() -> String {
        // Simplified network detection
        // In a real app, use Network framework or Reachability
        return "WiFi"
    }
}

// MARK: - CLLocationManagerDelegate
extension ProofViewModel: CLLocationManagerDelegate {
    nonisolated func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
        Task { @MainActor in
            currentLocation = locations.last
        }
    }

    nonisolated func locationManager(_ manager: CLLocationManager, didFailWithError error: Error) {
        print("Location error: \(error)")
    }
}