proofmode 0.9.0

Capture, share, and preserve verifiable photos and videos
Documentation
// ProofMode iOS Integration Example
// This file shows how to integrate the ProofMode library into your iOS app

import Foundation
import UIKit
import SwiftUI

// MARK: - Basic ProofMode Integration

class ProofModeManager: ObservableObject {
    private let proofMode = ProofMode()
    @Published var isInitialized = false
    @Published var proofs: [String] = []
    
    // Initialize ProofMode with your configuration
    func initialize() async {
        do {
            let documentsPath = FileManager.default.urls(for: .documentDirectory, 
                                                        in: .userDomainMask).first!
            let storagePath = documentsPath.appendingPathComponent("proofs").path
            
            try proofMode.initialize(
                storagePath: storagePath,
                email: "user@example.com",
                passphrase: "secure_passphrase"
            )
            
            await MainActor.run {
                isInitialized = true
            }
        } catch {
            print("Failed to initialize ProofMode: \(error)")
        }
    }
    
    // Generate proof for image data
    func generateProof(for imageData: Data, 
                      description: String? = nil,
                      location: CLLocation? = nil) async -> String? {
        guard isInitialized else {
            print("ProofMode not initialized")
            return nil
        }
        
        do {
            var metadata: [String: String] = [:]
            
            if let description = description {
                metadata["description"] = description
            }
            
            if let location = location {
                metadata["latitude"] = "\(location.coordinate.latitude)"
                metadata["longitude"] = "\(location.coordinate.longitude)"
                metadata["altitude"] = "\(location.altitude)"
                metadata["accuracy"] = "\(location.horizontalAccuracy)"
            }
            
            let proofHash = try proofMode.generateProof(
                mediaData: imageData,
                metadata: metadata.isEmpty ? nil : metadata
            )
            
            await MainActor.run {
                proofs.append(proofHash)
            }
            
            return proofHash
            
        } catch {
            print("Failed to generate proof: \(error)")
            return nil
        }
    }
    
    // Verify a proof
    func verifyProof(_ hash: String) async -> Bool {
        guard isInitialized else { return false }
        
        do {
            return try proofMode.verifyProof(hash: hash)
        } catch {
            print("Failed to verify proof: \(error)")
            return false
        }
    }
}

// MARK: - Camera Integration Example

struct CameraView: UIViewControllerRepresentable {
    @Binding var capturedImage: UIImage?
    @Environment(\.presentationMode) var presentationMode
    
    func makeUIViewController(context: Context) -> UIImagePickerController {
        let picker = UIImagePickerController()
        picker.delegate = context.coordinator
        picker.sourceType = .camera
        picker.allowsEditing = false
        return picker
    }
    
    func updateUIViewController(_ uiViewController: UIImagePickerController, context: Context) {}
    
    func makeCoordinator() -> Coordinator {
        Coordinator(self)
    }
    
    class Coordinator: NSObject, UIImagePickerControllerDelegate, UINavigationControllerDelegate {
        let parent: CameraView
        
        init(_ parent: CameraView) {
            self.parent = parent
        }
        
        func imagePickerController(_ picker: UIImagePickerController, 
                                 didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey : Any]) {
            if let image = info[.originalImage] as? UIImage {
                parent.capturedImage = image
            }
            parent.presentationMode.wrappedValue.dismiss()
        }
        
        func imagePickerControllerDidCancel(_ picker: UIImagePickerController) {
            parent.presentationMode.wrappedValue.dismiss()
        }
    }
}

// MARK: - Location Provider Implementation

class LocationProvider: NSObject, ObservableObject, CLLocationManagerDelegate {
    private let locationManager = CLLocationManager()
    @Published var currentLocation: CLLocation?
    @Published var authorizationStatus: CLAuthorizationStatus = .notDetermined
    
    override init() {
        super.init()
        locationManager.delegate = self
        locationManager.desiredAccuracy = kCLLocationAccuracyBest
    }
    
    func requestPermission() {
        locationManager.requestWhenInUseAuthorization()
    }
    
    func startLocationUpdates() {
        guard authorizationStatus == .authorizedWhenInUse || 
              authorizationStatus == .authorizedAlways else {
            requestPermission()
            return
        }
        locationManager.startUpdatingLocation()
    }
    
    func stopLocationUpdates() {
        locationManager.stopUpdatingLocation()
    }
    
    // MARK: - CLLocationManagerDelegate
    
    func locationManagerDidChangeAuthorization(_ manager: CLLocationManager) {
        authorizationStatus = manager.authorizationStatus
        
        if authorizationStatus == .authorizedWhenInUse || 
           authorizationStatus == .authorizedAlways {
            startLocationUpdates()
        }
    }
    
    func locationManager(_ manager: CLLocationManager, 
                        didUpdateLocations locations: [CLLocation]) {
        currentLocation = locations.last
    }
    
    func locationManager(_ manager: CLLocationManager, 
                        didFailWithError error: Error) {
        print("Location error: \(error)")
    }
}

// MARK: - Complete SwiftUI Example

struct ProofGenerationView: View {
    @StateObject private var proofMode = ProofModeManager()
    @StateObject private var locationProvider = LocationProvider()
    
    @State private var capturedImage: UIImage?
    @State private var showCamera = false
    @State private var showImagePicker = false
    @State private var description = ""
    @State private var isGeneratingProof = false
    @State private var lastProofHash: String?
    @State private var showAlert = false
    @State private var alertMessage = ""
    
    var body: some View {
        NavigationView {
            VStack(spacing: 20) {
                // Image Display
                if let image = capturedImage {
                    Image(uiImage: image)
                        .resizable()
                        .aspectRatio(contentMode: .fit)
                        .frame(maxHeight: 300)
                        .cornerRadius(12)
                } else {
                    RoundedRectangle(cornerRadius: 12)
                        .fill(Color.gray.opacity(0.3))
                        .frame(height: 300)
                        .overlay(
                            Text("No image selected")
                                .foregroundColor(.gray)
                        )
                }
                
                // Description Input
                TextField("Description (optional)", text: $description)
                    .textFieldStyle(RoundedBorderTextFieldStyle())
                
                // Action Buttons
                HStack(spacing: 15) {
                    Button("Take Photo") {
                        showCamera = true
                    }
                    .buttonStyle(.borderedProminent)
                    
                    Button("Choose Photo") {
                        showImagePicker = true
                    }
                    .buttonStyle(.bordered)
                }
                
                // Generate Proof Button
                Button(action: generateProof) {
                    HStack {
                        if isGeneratingProof {
                            ProgressView()
                                .scaleEffect(0.8)
                        }
                        Text(isGeneratingProof ? "Generating..." : "Generate Proof")
                    }
                }
                .buttonStyle(.borderedProminent)
                .disabled(capturedImage == nil || isGeneratingProof || !proofMode.isInitialized)
                
                // Status Display
                if let hash = lastProofHash {
                    VStack {
                        Text("Proof Generated")
                            .font(.headline)
                            .foregroundColor(.green)
                        
                        Text("Hash: \(hash.prefix(16))...")
                            .font(.caption)
                            .foregroundColor(.secondary)
                    }
                    .padding()
                    .background(Color.green.opacity(0.1))
                    .cornerRadius(8)
                }
                
                Spacer()
            }
            .padding()
            .navigationTitle("ProofMode")
            .sheet(isPresented: $showCamera) {
                CameraView(capturedImage: $capturedImage)
            }
            .sheet(isPresented: $showImagePicker) {
                ImagePicker(selectedImage: $capturedImage)
            }
            .alert("ProofMode", isPresented: $showAlert) {
                Button("OK") { }
            } message: {
                Text(alertMessage)
            }
            .onAppear {
                setupProofMode()
            }
        }
    }
    
    private func setupProofMode() {
        Task {
            await proofMode.initialize()
        }
        locationProvider.requestPermission()
    }
    
    private func generateProof() {
        guard let image = capturedImage,
              let imageData = image.jpegData(compressionQuality: 0.8) else {
            showAlert(message: "No image data available")
            return
        }
        
        isGeneratingProof = true
        
        Task {
            let hash = await proofMode.generateProof(
                for: imageData,
                description: description.isEmpty ? nil : description,
                location: locationProvider.currentLocation
            )
            
            await MainActor.run {
                isGeneratingProof = false
                
                if let hash = hash {
                    lastProofHash = hash
                    showAlert(message: "Proof generated successfully!\nHash: \(hash)")
                } else {
                    showAlert(message: "Failed to generate proof")
                }
            }
        }
    }
    
    private func showAlert(message: String) {
        alertMessage = message
        showAlert = true
    }
}

// MARK: - Image Picker Helper

struct ImagePicker: UIViewControllerRepresentable {
    @Binding var selectedImage: UIImage?
    @Environment(\.presentationMode) var presentationMode
    
    func makeUIViewController(context: Context) -> UIImagePickerController {
        let picker = UIImagePickerController()
        picker.delegate = context.coordinator
        picker.sourceType = .photoLibrary
        return picker
    }
    
    func updateUIViewController(_ uiViewController: UIImagePickerController, context: Context) {}
    
    func makeCoordinator() -> Coordinator {
        Coordinator(self)
    }
    
    class Coordinator: NSObject, UIImagePickerControllerDelegate, UINavigationControllerDelegate {
        let parent: ImagePicker
        
        init(_ parent: ImagePicker) {
            self.parent = parent
        }
        
        func imagePickerController(_ picker: UIImagePickerController,
                                 didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey : Any]) {
            if let image = info[.originalImage] as? UIImage {
                parent.selectedImage = image
            }
            parent.presentationMode.wrappedValue.dismiss()
        }
    }
}

// MARK: - App Integration Notes

/*
 To integrate this into your app:
 
 1. Add the ProofMode XCFramework to your project
 2. Copy the generated Swift bindings
 3. Add the required permissions to Info.plist:
    - NSCameraUsageDescription
    - NSPhotoLibraryUsageDescription  
    - NSLocationWhenInUseUsageDescription
 
 4. Initialize ProofMode early in your app lifecycle:
    ```swift
    @main
    struct MyApp: App {
        var body: some Scene {
            WindowGroup {
                ContentView()
                    .onAppear {
                        // Initialize ProofMode
                    }
            }
        }
    }
    ```
 
 5. Handle background processing if needed:
    - Add background modes capability
    - Handle app lifecycle events
    - Implement proper cleanup
 
 6. Consider security:
    - Store PGP keys securely (Keychain)
    - Validate user permissions
    - Handle sensitive data appropriately
    - Implement proper error recovery
 */