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)")
}
}