JAAK Stamps SDK iOS
@jaak.ai/stamps sdk ios - Componente para la captura de identificaciones
⏱️ Tiempo estimado: 45-60 minutos para configuración completa
🎯 ¿Qué aprenderás en este manual?
Este manual te enseñará a implementar y usar el JAAKStamps SDK para captura automatizada de documentos de identidad en aplicaciones iOS nativas utilizando inteligencia artificial y visión por computadora. Requiere conocimientos avanzados en desarrollo iOS, frameworks nativos, manejo de cámara y arquitectura de aplicaciones móviles.
📋 Antes de Empezar - Lista de Verificación
Asegúrate de tener estos elementos listos:
- Xcode 12.0+ instalado
- Dispositivo iOS 12.0+ (físico, no simulador)
- CocoaPods configurado
- Conocimientos avanzados de Swift/UIKit
- Experiencia con AVFoundation y Core ML
- Acceso a cámara funcional
- Documento de identidad para pruebas
🗂️ Índice de Contenidos
Sección | Qué harás | Tiempo |
---|---|---|
Paso 1 | Configurar proyecto y dependencias | 15 min |
Paso 2 | Implementación básica del SDK | 25 min |
Paso 3 | Configurar permisos de cámara | 10 min |
Paso 4 | Manejo de respuestas y imágenes | 20 min |
Paso 5 | Implementación avanzada (opcional) | 30 min |
Paso 6 | Probar captura de documentos | 15 min |
PASO 1: Configurar Proyecto y Dependencias
🎯 Objetivo
Configurar el entorno de desarrollo y añadir las dependencias necesarias del SDK.
✅ Requisitos Técnicos
Requisito | Versión | ¿Obligatorio? | Notas |
---|---|---|---|
iOS | 12.0+ | Sí | Compatible desde iOS 12.0 |
Xcode | 12.0+ | Sí | IDE de desarrollo |
Swift | 5.5+ | Sí | Lenguaje de programación |
CocoaPods | Latest | Sí | Gestor de dependencias |
Dispositivo | iPhone/iPad con cámara | Sí | Captura requiere cámara |
RAM | 2GB+ | Recomendado | Para procesamiento ML |
1.1 Configuración Podfile
platform :ios, '12.0'
use_frameworks!
target 'YourApp' do
pod 'JAAKStamps', '~> 1.0.0'
end
1.2 Instalación de Dependencias
# Instalar dependencias
pod install
# Abrir workspace (importante: no el .xcodeproj)
open YourApp.xcworkspace
1.3 Frameworks Requeridos
En tu proyecto Xcode, verifica que estos frameworks estén linkeados:
- UIKit - Interfaz de usuario
- AVFoundation - Acceso a cámara y captura
- Foundation - Funcionalidades base
1.4 Dependencias Externas
El SDK incluye automáticamente:
- onnxruntime-objc ~> 1.16.0 (procesamiento de modelos ONNX)
PASO 2: Implementación Básica del SDK
🎯 Objetivo
Crear la implementación base del JAAKStamps SDK con captura automatizada.
2.1 ViewController Básico - UIKit (Recomendado)
import UIKit
import JAAKStamps
class DocumentCaptureViewController: UIViewController {
private var jaakStampsController: JaakStampsViewController?
override func viewDidLoad() {
super.viewDidLoad()
setupDocumentCapture()
}
private func setupDocumentCapture() {
// Verificar compatibilidad del dispositivo
guard JAAKStampsSDK.isCompatible() else {
showCompatibilityError()
return
}
// Configuración básica con valores por defecto
let config = JaakStampsConfig.defaultConfig()
// Crear controlador embebido
jaakStampsController = JaakStampsViewController(config: config)
jaakStampsController?.delegate = self
// Embeber como child view controller
guard let captureController = jaakStampsController else { return }
addChild(captureController)
view.addSubview(captureController.view)
captureController.view.frame = view.bounds
captureController.didMove(toParent: self)
// Iniciar captura automáticamente
captureController.startCapture()
}
private func showCompatibilityError() {
let alert = UIAlertController(
title: "Dispositivo No Compatible",
message: "Este dispositivo no es compatible con el SDK JAAKStamps",
preferredStyle: .alert
)
alert.addAction(UIAlertAction(title: "Entendido", style: .default))
present(alert, animated: true)
}
}
// MARK: - JaakStampsDelegate
extension DocumentCaptureViewController: JaakStampsDelegate {
func jaakStampsDidBecomeReady(_ controller: JaakStampsViewController) {
print("SDK listo para captura")
}
func jaakStamps(_ controller: JaakStampsViewController, didCompleteCapture images: CapturedImages) {
print("Captura completada exitosamente")
// Procesar imágenes frontales
if let frontImage = images.front.fullFrame {
processImage(frontImage, type: "front_full")
}
if let frontCropped = images.front.cropped {
processImage(frontCropped, type: "front_cropped")
}
// Procesar imágenes traseras (si existen)
if let backImage = images.back.fullFrame {
processImage(backImage, type: "back_full")
}
if let backCropped = images.back.cropped {
processImage(backCropped, type: "back_cropped")
}
// Mostrar metadatos
let metadata = images.metadata
print("Total imágenes: \(metadata.totalImages)")
print("Reverso omitido: \(metadata.backCaptureSkipped)")
print("Tiempo de procesamiento: \(metadata.processingTime)s")
// Cerrar captura
controller.dismiss(animated: true)
}
func jaakStamps(_ controller: JaakStampsViewController, didFailWithError error: JaakStampsError) {
print("Error en captura: \(error.localizedDescription)")
handleCaptureError(error)
controller.dismiss(animated: true)
}
func jaakStamps(_ controller: JaakStampsViewController, didUpdateStatus status: ComponentStatus) {
// Opcional: manejar actualizaciones de estado
print("Estado actual: \(status.captureStep), Alineado: \(status.isAligned)")
}
private func processImage(_ image: UIImage, type: String) {
print("Procesando imagen: \(type)")
// Convertir a base64 si es necesario
// let base64 = image.jpegData(compressionQuality: 0.8)?.base64EncodedString()
}
private func handleCaptureError(_ error: JaakStampsError) {
var title = "Error"
var message = error.localizedDescription
switch error {
case .cameraPermissionDenied:
title = "Permisos de Cámara"
message = "Se requiere acceso a la cámara para capturar documentos"
case .cameraNotAvailable:
title = "Cámara No Disponible"
message = "No se pudo acceder a la cámara del dispositivo"
case .modelLoadingFailed(let details):
title = "Error de Modelo ML"
message = "Error cargando modelo: \(details)"
case .captureSessionFailed(let details):
title = "Error de Captura"
message = "Error en sesión de captura: \(details)"
default:
break
}
let alert = UIAlertController(title: title, message: message, preferredStyle: .alert)
alert.addAction(UIAlertAction(title: "Entendido", style: .default))
present(alert, animated: true)
}
}
2.2 Implementación SwiftUI
import SwiftUI
import JAAKStamps
// Manager para controlar el SDK
class DocumentCaptureManager: ObservableObject {
@Published var capturedImages: CapturedImages?
@Published var errorMessage: String?
@Published var isReady = false
private var jaakStampsController: JaakStampsViewController?
var captureViewController: JaakStampsViewController? {
return jaakStampsController
}
func initialize() {
guard jaakStampsController == nil else { return }
guard JAAKStampsSDK.isCompatible() else {
errorMessage = "Dispositivo no compatible"
return
}
let config = JaakStampsConfig.defaultConfig()
jaakStampsController = JaakStampsViewController(config: config)
jaakStampsController?.delegate = self
}
}
// MARK: - JaakStampsDelegate
extension DocumentCaptureManager: JaakStampsDelegate {
func jaakStampsDidBecomeReady(_ controller: JaakStampsViewController) {
DispatchQueue.main.async {
self.isReady = true
print("SDK listo para captura")
}
}
func jaakStamps(_ controller: JaakStampsViewController, didCompleteCapture images: CapturedImages) {
DispatchQueue.main.async {
self.capturedImages = images
print("Captura completada exitosamente")
}
}
func jaakStamps(_ controller: JaakStampsViewController, didFailWithError error: JaakStampsError) {
DispatchQueue.main.async {
self.errorMessage = error.localizedDescription
print("Error en captura: \(error.localizedDescription)")
}
}
func jaakStamps(_ controller: JaakStampsViewController, didUpdateStatus status: ComponentStatus) {
// Opcional: manejar actualizaciones de estado
}
}
// Wrapper para integrar JAAKStamps en SwiftUI
struct JAAKStampsView: UIViewControllerRepresentable {
@ObservedObject var captureManager: DocumentCaptureManager
func makeUIViewController(context: Context) -> JaakStampsViewController {
if captureManager.captureViewController == nil {
captureManager.initialize()
}
guard let controller = captureManager.captureViewController else {
fatalError("JAAKStamps controller should exist after initialization")
}
return controller
}
func updateUIViewController(_ uiViewController: JaakStampsViewController, context: Context) {
// SwiftUI manejará las actualizaciones automáticamente
}
}
// Vista básica de SwiftUI
struct ContentView: View {
@ObservedObject private var captureManager = DocumentCaptureManager()
var body: some View {
VStack(spacing: 20) {
Text("Captura de Documentos")
.font(.title)
.fontWeight(.bold)
if captureManager.isReady {
// Vista de cámara embebida
JAAKStampsView(captureManager: captureManager)
.frame(height: 400)
.cornerRadius(12)
// Botón de captura
Button("Iniciar Captura") {
captureManager.captureViewController?.startCapture()
}
.padding()
.background(Color.blue)
.foregroundColor(.white)
.cornerRadius(8)
}
// Mostrar resultado
if let images = captureManager.capturedImages {
Text("¡Documento capturado exitosamente!")
.foregroundColor(.green)
.padding()
}
if let error = captureManager.errorMessage {
Text("Error: \(error)")
.foregroundColor(.red)
.padding()
}
Spacer()
}
.padding()
.onAppear {
captureManager.initialize()
}
}
}
2.3 Diferencias entre UIKit y SwiftUI
Aspecto | UIKit | SwiftUI |
---|---|---|
Integración | Directa y nativa | Requiere UIViewControllerRepresentable |
Complejidad | Más simple, menos código | Más código de wrapper necesario |
Rendimiento | Óptimo, sin overhead | Ligero overhead por el wrapper |
Manejo de estado | Delegados tradicionales | Binding y coordinadores |
Presentación | Modal o embedded directo | FullScreenCover o sheet |
Debugging | Más directo | Puede ser más complejo |
Recomendación | Preferida para este SDK | Usar solo si la app es 100% SwiftUI |
💡 Recomendación: Usa la implementación UIKit cuando sea posible, ya que el SDK está optimizado para este framework.
PASO 3: Configurar Permisos de Cámara
🎯 Objetivo
Configurar correctamente los permisos de cámara requeridos por iOS.
3.1 Configuración Info.plist (Método Tradicional)
<key>NSCameraUsageDescription</key>
<string>Esta aplicación necesita acceso a la cámara para capturar documentos</string>
3.2 Configuración en Xcode (Versiones Recientes)
- En Xcode, selecciona tu target
- Ve a la pestaña Info
- Expande Custom iOS Target Properties
- Haz clic en el botón + para agregar una nueva key
- Selecciona Privacy - Camera Usage Description
- Agrega el valor:
Esta aplicación necesita acceso a la cámara para capturar documentos
3.3 Privacy Manifest (iOS 17+)
<!-- PrivacyInfo.xcprivacy -->
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>NSPrivacyAccessedAPITypes</key>
<array>
<dict>
<key>NSPrivacyAccessedAPIType</key>
<string>NSPrivacyAccessedAPICategoryCamera</string>
<key>NSPrivacyAccessedAPITypeReasons</key>
<array>
<string>CA92.1</string>
</array>
</dict>
</array>
<key>NSPrivacyCollectedDataTypes</key>
<array/>
<key>NSPrivacyTracking</key>
<false/>
<key>NSPrivacyTrackingDomains</key>
<array/>
</dict>
</plist>
3.4 Manejo de Permisos en Código
import AVFoundation
class PermissionsManager {
static func checkCameraPermission() -> AVAuthorizationStatus {
return AVCaptureDevice.authorizationStatus(for: .video)
}
static func requestCameraPermission(completion: @escaping (Bool) -> Void) {
AVCaptureDevice.requestAccess(for: .video) { granted in
DispatchQueue.main.async {
completion(granted)
}
}
}
static func openSettings() {
guard let settingsUrl = URL(string: UIApplication.openSettingsURLString) else {
return
}
if UIApplication.shared.canOpenURL(settingsUrl) {
UIApplication.shared.open(settingsUrl)
}
}
}
3.5 Verificación Antes de Captura
private func setupDocumentCapture() {
let cameraStatus = PermissionsManager.checkCameraPermission()
switch cameraStatus {
case .authorized:
startDocumentCapture()
case .notDetermined:
PermissionsManager.requestCameraPermission { granted in
if granted {
self.startDocumentCapture()
} else {
self.showPermissionAlert()
}
}
case .denied, .restricted:
showPermissionAlert()
@unknown default:
showPermissionAlert()
}
}
private func startDocumentCapture() {
guard JAAKStampsSDK.isCompatible() else {
showCompatibilityError()
return
}
let config = JaakStampsConfig.defaultConfig()
let captureController = JaakStampsViewController(config: config)
captureController.delegate = self
captureController.modalPresentationStyle = .fullScreen
present(captureController, animated: true) {
captureController.startCapture()
}
}
private func showPermissionAlert() {
let alert = UIAlertController(
title: "Permisos de Cámara",
message: "Se requiere acceso a la cámara para capturar documentos",
preferredStyle: .alert
)
alert.addAction(UIAlertAction(title: "Configuración", style: .default) { _ in
PermissionsManager.openSettings()
})
alert.addAction(UIAlertAction(title: "Cancelar", style: .cancel))
present(alert, animated: true)
}
PASO 4: Manejo de Respuestas e Imágenes
🎯 Objetivo
Implementar el manejo completo de las imágenes capturadas según la estructura oficial del SDK.
4.1 Estructura de Respuesta Completa
// Estructura oficial del SDK
public struct CapturedImages {
public let front: ImagePair // Imagen frontal
public let back: ImagePair // Imagen trasera
public let metadata: Metadata // Metadatos de captura
public struct ImagePair {
public let fullFrame: UIImage? // Imagen completa
public let cropped: UIImage? // Imagen recortada
}
public struct Metadata {
public let totalImages: Int // Total de imágenes
public let processCompleted: Bool // Proceso completado
public let backCaptureSkipped: Bool // Reverso omitido
public let timestamp: Date // Marca de tiempo
public let captureSteps: [CaptureStep] // Pasos completados
public let documentType: DocumentType? // Tipo de documento
public let processingTime: TimeInterval // Tiempo de procesamiento
}
}
4.2 Procesamiento Completo de Imágenes
func jaakStamps(_ controller: JaakStampsViewController, didCompleteCapture images: CapturedImages) {
let documentImages = DocumentImages()
// Procesar frente completo
if let frontFull = images.front.fullFrame {
documentImages.frontOriginal = convertToBase64(frontFull)
print("Front Original: \(documentImages.frontOriginal?.count ?? 0) chars")
}
// Procesar frente recortado
if let frontCrop = images.front.cropped {
documentImages.frontCrop = convertToBase64(frontCrop)
print("Front Crop: \(documentImages.frontCrop?.count ?? 0) chars")
}
// Procesar reverso completo (si existe)
if let backFull = images.back.fullFrame {
documentImages.backOriginal = convertToBase64(backFull)
print("Back Original: \(documentImages.backOriginal?.count ?? 0) chars")
}
// Procesar reverso recortado (si existe)
if let backCrop = images.back.cropped {
documentImages.backCrop = convertToBase64(backCrop)
print("Back Crop: \(documentImages.backCrop?.count ?? 0) chars")
}
// Determinar tipo de documento
let documentType = images.metadata.backCaptureSkipped ? "single_sided" : "double_sided"
// Procesar documento completo
processCompleteDocument(documentImages, type: documentType, metadata: images.metadata)
controller.dismiss(animated: true)
}
struct DocumentImages {
var frontOriginal: String?
var frontCrop: String?
var backOriginal: String?
var backCrop: String?
}
private func convertToBase64(_ image: UIImage) -> String? {
guard let imageData = image.jpegData(compressionQuality: 0.8) else { return nil }
return imageData.base64EncodedString()
}
private func processCompleteDocument(_ images: DocumentImages, type: String, metadata: CapturedImages.Metadata) {
print("=== PROCESANDO DOCUMENTO ===")
print("Tipo: \(type)")
print("Imágenes totales: \(metadata.totalImages)")
print("Proceso completado: \(metadata.processCompleted)")
print("Tiempo de procesamiento: \(metadata.processingTime)s")
// Validar que tenemos al menos la imagen frontal
guard images.frontOriginal != nil || images.frontCrop != nil else {
print("Error: No se encontraron imágenes frontales válidas")
return
}
// Crear payload para backend
let payload = createDocumentPayload(images, type: type, metadata: metadata)
// Enviar a backend o procesar localmente
sendToBackend(payload)
print("Documento procesado correctamente")
}
private func createDocumentPayload(_ images: DocumentImages, type: String, metadata: CapturedImages.Metadata) -> [String: Any] {
return [
"documentType": type,
"timestamp": Date().timeIntervalSince1970,
"version": "1.0.0",
"images": [
"frontOriginal": images.frontOriginal ?? "",
"frontCrop": images.frontCrop ?? "",
"backOriginal": images.backOriginal ?? "",
"backCrop": images.backCrop ?? ""
],
"metadata": [
"totalImages": metadata.totalImages,
"backCaptureSkipped": metadata.backCaptureSkipped,
"processingTime": metadata.processingTime,
"documentType": metadata.documentType?.rawValue ?? "unknown",
"platform": "ios"
]
]
}
private func sendToBackend(_ payload: [String: Any]) {
print("Enviando a backend: \(payload.keys)")
// Implementar llamada a tu API
}
4.3 Manejo Completo de Errores
func jaakStamps(_ controller: JaakStampsViewController, didFailWithError error: JaakStampsError) {
print("Error en captura: \(error.localizedDescription)")
switch error {
case .cameraPermissionDenied:
handleCameraPermissionError()
case .cameraNotAvailable:
handleCameraError()
case .modelLoadingFailed(let message):
handleMLError(message)
case .captureSessionFailed(let message):
handleCaptureError(message)
case .processingFailed(let message):
handleProcessingError(message)
case .networkError(let message):
handleNetworkError(message)
case .invalidConfiguration(let message):
handleConfigurationError(message)
case .unknown(let message):
handleGenericError(message)
}
controller.dismiss(animated: true)
}
private func handleCameraPermissionError() {
showAlert(
title: "Permisos de Cámara",
message: "Se requiere acceso a la cámara para capturar documentos. Ve a Configuración para habilitarlo."
) {
PermissionsManager.openSettings()
}
}
private func handleCameraError() {
showAlert(
title: "Error de Cámara",
message: "No se pudo acceder a la cámara. Verifica que no esté siendo usada por otra aplicación."
)
}
private func handleMLError(_ message: String) {
showAlert(
title: "Error de Procesamiento",
message: "No se pudo cargar el modelo de IA: \(message). Reinicia la aplicación e intenta nuevamente."
)
}
private func handleCaptureError(_ message: String) {
showAlert(
title: "Error de Captura",
message: "Error durante la captura: \(message). Intenta nuevamente."
)
}
private func handleProcessingError(_ message: String) {
showAlert(
title: "Error de Procesamiento",
message: "Error al procesar la imagen: \(message). Verifica que el documento esté bien iluminado."
)
}
private func handleNetworkError(_ message: String) {
showAlert(
title: "Error de Red",
message: "Error de conectividad: \(message). Verifica tu conexión a internet."
)
}
private func handleConfigurationError(_ message: String) {
showAlert(
title: "Error de Configuración",
message: "Configuración inválida: \(message). Contacta al desarrollador."
)
}
private func handleGenericError(_ message: String) {
showAlert(
title: "Error",
message: "Ocurrió un error inesperado: \(message)"
)
}
private func showAlert(title: String, message: String, action: (() -> Void)? = nil) {
let alert = UIAlertController(title: title, message: message, preferredStyle: .alert)
if let action = action {
alert.addAction(UIAlertAction(title: "Configuración", style: .default) { _ in
action()
})
alert.addAction(UIAlertAction(title: "Cancelar", style: .cancel))
} else {
alert.addAction(UIAlertAction(title: "Entendido", style: .default))
}
present(alert, animated: true)
}
PASO 5: Implementación Avanzada (Opcional)
🎯 Objetivo
Implementar funcionalidades avanzadas del SDK con configuración personalizada y delegados adicionales.
5.1 Configuración Avanzada
private func initializeAdvancedSDK() {
// Configuración avanzada personalizada
var config = JaakStampsConfig()
config.debug = true // Modo debug para desarrollo
config.maskSize = 85.0 // Tamaño de máscara (50-100)
config.alignmentTolerance = 20.0 // Tolerancia de alineación
config.cropMargin = 0.0 // Margen de recorte (0-100)
config.autoCapture = true // Captura automática
config.autoCaptureDelay = 1.5 // Delay para captura automática
config.useDocumentClassification = true // Clasificación de documentos
config.preferredCamera = .back // Cámara preferida
config.maxFramesPerSecond = 30 // FPS máximo
config.minConfidenceThreshold = 0.7 // Umbral de confianza mínimo
// Inicializar controlador
jaakStampsController = JaakStampsViewController(config: config)
jaakStampsController.delegate = self
// Precargar modelo para mejor rendimiento
jaakStampsController.preloadModel { [weak self] success in
DispatchQueue.main.async {
if success {
self?.statusLabel.text = "Modelo precargado - Listo para capturar"
} else {
self?.statusLabel.text = "Error precargando modelo"
}
}
}
}
5.2 Configuración Dinámica
// Métodos para actualizar configuración en tiempo real
private func updateMaskSize(_ size: Double) {
jaakStampsController.setMaskSize(size)
print("Tamaño de máscara actualizado a \(size)")
}
private func updateAlignmentTolerance(_ tolerance: Double) {
jaakStampsController.setAlignmentTolerance(tolerance)
print("Tolerancia de alineación actualizada a \(tolerance)")
}
private func updateCropMargin(_ margin: Double) {
jaakStampsController.setCropMargin(margin)
print("Margen de recorte actualizado a \(margin)")
}
private func toggleDebugMode(_ enabled: Bool) {
jaakStampsController.setDebugMode(enabled)
print("Modo debug \(enabled ? "activado" : "desactivado")")
}
private func changeCameraPreference(_ preference: CameraPreference) {
jaakStampsController.setPreferredCamera(preference)
print("Cámara cambiada a \(preference)")
}
// Reiniciar componente
private func resetComponent() {
jaakStampsController.resetComponent()
print("Componente reiniciado")
}
5.3 Delegados Avanzados
// MARK: - JaakStampsDelegate (Métodos Avanzados)
extension AdvancedDocumentCaptureViewController: JaakStampsDelegate {
func jaakStampsDidBecomeReady(_ controller: JaakStampsViewController) {
DispatchQueue.main.async {
self.statusLabel.text = "Cámara lista - Posicione el documento"
}
}
func jaakStamps(_ controller: JaakStampsViewController, didCompleteCapture images: CapturedImages) {
DispatchQueue.main.async {
let totalImages = images.metadata.totalImages
let frontAvailable = images.front.fullFrame != nil
let backAvailable = images.back.fullFrame != nil
let processingTime = images.metadata.processingTime
self.statusLabel.text = """
✅ Captura completada exitosamente!
Imágenes: \(totalImages) | Frontal: \(frontAvailable) | Trasera: \(backAvailable)
Tiempo: \(String(format: "%.2f", processingTime))s
"""
print("=== CAPTURA COMPLETADA ===")
print("Total de imágenes: \(totalImages)")
print("Imagen frontal completa: \(images.front.fullFrame != nil)")
print("Imagen frontal recortada: \(images.front.cropped != nil)")
print("Imagen trasera disponible: \(backAvailable)")
print("Captura trasera omitida: \(images.metadata.backCaptureSkipped)")
print("Tiempo de procesamiento: \(processingTime)s")
print("Tipo de documento: \(images.metadata.documentType?.rawValue ?? "No detectado")")
}
}
func jaakStamps(_ controller: JaakStampsViewController, didFailWithError error: JaakStampsError) {
DispatchQueue.main.async {
self.statusLabel.text = "❌ Error: \(error.localizedDescription)"
// Mostrar sugerencia de recuperación si está disponible
if error.isRecoverable {
self.statusLabel.text += "\n💡 \(error.recoveryAction)"
}
}
}
func jaakStamps(_ controller: JaakStampsViewController, didUpdateStatus status: ComponentStatus) {
DispatchQueue.main.async {
// Actualizar progreso de alineación
let alignmentScore = status.isAligned ? 1.0 : 0.3
self.alignmentProgressView.progress = Float(alignmentScore)
// Actualizar mensaje de estado basado en el paso actual
switch status.captureStep {
case .front:
if status.isAligned {
self.statusLabel.text = "✅ Frontal alineado - Capturando..."
} else {
self.statusLabel.text = "🔄 Posicione la parte frontal del documento"
}
case .transitioning:
self.statusLabel.text = "🔄 Por favor voltee el documento"
case .back:
if status.isAligned {
self.statusLabel.text = "✅ Trasero alineado - Capturando..."
} else {
self.statusLabel.text = "🔄 Posicione la parte trasera del documento"
}
case .completed:
self.statusLabel.text = "✅ Captura completada"
}
// Mostrar información adicional de debug
if self.debugMode {
let confidence = String(format: "%.2f", status.detectionConfidence)
self.statusLabel.text += "\n🔍 Confianza: \(confidence)"
}
}
}
func jaakStamps(_ controller: JaakStampsViewController, didUpdateAlignment alignment: SideAlignment) {
DispatchQueue.main.async {
// Feedback visual para alineación
let alignmentText = """
Alineación: ↑\(alignment.top ? "✅" : "❌") ↓\(alignment.bottom ? "✅" : "❌") ←\(alignment.left ? "✅" : "❌") →\(alignment.right ? "✅" : "❌")
"""
if self.debugMode {
self.statusLabel.text += "\n\(alignmentText)"
}
// Actualizar progreso basado en alineación
let alignedSides = [alignment.top, alignment.bottom, alignment.left, alignment.right].compactMap { $0 ? 1 : 0 }.reduce(0, +)
self.alignmentProgressView.progress = Float(alignedSides) / 4.0
}
}
func jaakStamps(_ controller: JaakStampsViewController, didClassifyDocument classification: ClassificationResult) {
DispatchQueue.main.async {
let confidencePercent = Int(classification.confidence * 100)
self.statusLabel.text = "📋 Documento clasificado: \(classification.documentType) (\(confidencePercent)%)"
}
}
func jaakStamps(_ controller: JaakStampsViewController, didChangeCamera cameraInfo: CameraInfo) {
DispatchQueue.main.async {
let deviceCount = cameraInfo.availableCameras.count
let selectedCamera = cameraInfo.selectedCameraId ?? "No seleccionada"
self.statusLabel.text = """
📷 Cámara cambiada
Dispositivos disponibles: \(deviceCount) | Seleccionada: \(selectedCamera)
"""
}
}
}
### **5.4 Utilidades del SDK**
```swift
// Verificaciones del sistema
private func checkSystemCapabilities() {
let isCompatible = JAAKStampsSDK.isCompatible()
let systemReqs = JAAKStampsSDK.getSystemRequirements()
let cameraStatus = JAAKStampsSDK.getCameraPermissionStatus()
let availableCameras = JAAKStampsSDK.getAvailableCameraDevices()
print("=== VERIFICACIÓN DEL SISTEMA ===")
print("SDK compatible: \(isCompatible)")
print("Requisitos del sistema: \(systemReqs)")
print("Estado de cámara: \(cameraStatus)")
print("Cámaras disponibles: \(availableCameras.count)")
}
// Solicitar permisos
private func requestCameraPermissionIfNeeded() {
JAAKStampsSDK.requestCameraPermission { granted in
DispatchQueue.main.async {
if granted {
self.startDocumentCapture()
} else {
self.showPermissionAlert()
}
}
}
}
5.5 Extensiones de Conveniencia
extension JaakStampsError {
var isRecoverable: Bool {
switch self {
case .cameraPermissionDenied, .cameraNotAvailable:
return true
case .modelLoadingFailed, .captureSessionFailed:
return true
case .processingFailed:
return true
default:
return false
}
}
var recoveryAction: String {
switch self {
case .cameraPermissionDenied:
return "Ve a Configuración para habilitar el acceso a la cámara"
case .cameraNotAvailable:
return "Cierra otras apps que usen la cámara e intenta nuevamente"
case .modelLoadingFailed:
return "Reinicia la aplicación e intenta nuevamente"
case .captureSessionFailed:
return "Reinicia la captura"
case .processingFailed:
return "Mejora la iluminación e intenta nuevamente"
default:
return "Intenta nuevamente"
}
}
}
extension CapturedImages {
var hasBothSides: Bool {
return front.fullFrame != nil && back.fullFrame != nil
}
var allImages: [UIImage] {
var images: [UIImage] = []
if let frontFull = front.fullFrame { images.append(frontFull) }
if let frontCrop = front.cropped { images.append(frontCrop) }
if let backFull = back.fullFrame { images.append(backFull) }
if let backCrop = back.cropped { images.append(backCrop) }
return images
}
}
PASO 6: Probar Captura de Documentos
🎯 Objetivo
Verificar que todas las funcionalidades del SDK funcionan correctamente.
✅ Lista de Verificación
Elemento | ✅ | Descripción |
---|---|---|
Compatibilidad del dispositivo | ☐ | SDK.isCompatible() retorna true |
Permisos de cámara | ☐ | App solicita y obtiene permisos |
Detección automática | ☐ | IA detecta documentos automáticamente |
Captura de imágenes | ☐ | Genera original y crop de frente/reverso |
Conversión base64 | ☐ | Todas las imágenes se convierten |
Manejo de errores | ☐ | Errores se manejan correctamente |
Configuración dinámica | ☐ | Métodos de configuración funcionan |
Delegados avanzados | ☐ | Callbacks adicionales funcionan |
🔍 Proceso de Prueba
Paso A: Verificar Compatibilidad
// Agregar a tu código de prueba
private func testCompatibility() {
print("=== PRUEBA DE COMPATIBILIDAD ===")
let isCompatible = JAAKStampsSDK.isCompatible()
print("SDK compatible: \(isCompatible)")
let systemReqs = JAAKStampsSDK.getSystemRequirements()
print("Requisitos: \(systemReqs)")
let cameraStatus = JAAKStampsSDK.getCameraPermissionStatus()
print("Estado cámara: \(cameraStatus)")
assert(isCompatible, "El dispositivo debe ser compatible")
}
Paso B: Probar Permisos
- Instalar aplicación en dispositivo físico
- Abrir aplicación por primera vez
- Verificar solicitud de permisos automática
- Conceder permisos de cámara
- Confirmar que la cámara inicia correctamente
Paso C: Probar Captura Simple
- Presionar botón de captura
- Posicionar documento dentro del marco
- Esperar detección automática (marco verde)
- Verificar captura automática
- Revisar logs para confirmar procesamiento
Paso D: Probar Documento Doble Cara
- Capturar frente del documento
- Seguir instrucciones para voltear
- Capturar reverso automáticamente
- Verificar 4 imágenes en logs (front/back full/cropped)
- Confirmar procesamiento exitoso
Paso E: Probar Configuración Avanzada
private func testAdvancedConfiguration() {
// Cambiar tamaño de máscara
jaakStampsController.setMaskSize(90.0)
// Cambiar tolerancia de alineación
jaakStampsController.setAlignmentTolerance(15.0)
// Activar modo debug
jaakStampsController.setDebugMode(true)
// Cambiar cámara
jaakStampsController.setPreferredCamera(.front)
print("Configuración avanzada aplicada")
}
🛠️ Debugging y Troubleshooting
Verificar Estado del SDK
private func debugSDKState() {
print("=== DEBUG DEL SDK ===")
// Verificar compatibilidad
let isCompatible = JAAKStampsSDK.isCompatible()
print("Compatible: \(isCompatible)")
// Verificar estado de la cámara
let cameraStatus = JAAKStampsSDK.getCameraPermissionStatus()
print("Permisos cámara: \(cameraStatus)")
// Verificar dispositivos disponibles
let cameras = JAAKStampsSDK.getAvailableCameraDevices()
print("Cámaras disponibles: \(cameras.count)")
// Verificar configuración actual
if let controller = jaakStampsController {
print("Controlador inicializado: Sí")
print("Modo debug: \(controller.isDebugModeEnabled)")
} else {
print("Controlador inicializado: No")
}
}
Habilitar Logging Detallado
private func enableDetailedLogging() {
var config = JaakStampsConfig()
config.debug = true
config.logLevel = .verbose
jaakStampsController = JaakStampsViewController(config: config)
jaakStampsController.delegate = self
}
Problemas Comunes y Soluciones
Problema | Causa Probable | Solución |
---|---|---|
"SDK no compatible" | iOS < 12.0 o hardware insuficiente | ✅ Verificar JAAKStampsSDK.isCompatible() |
"Cámara no inicia" | Permisos denegados o cámara ocupada | ✅ Verificar permisos y cerrar otras apps |
"Modelo no carga" | Memoria insuficiente o archivos corruptos | ✅ Reiniciar app y verificar espacio |
"Captura no funciona" | Simulador o configuración incorrecta | ✅ Usar dispositivo físico y verificar config |
"Detección lenta" | Configuración de FPS alta | ✅ Reducir maxFramesPerSecond |
"Imágenes borrosas" | Iluminación pobre o movimiento | ✅ Mejorar iluminación y estabilizar dispositivo |
Test de Integración Completa
func testCompleteIntegration() {
// 1. Verificar compatibilidad
XCTAssertTrue(JAAKStampsSDK.isCompatible())
// 2. Crear configuración
let config = JaakStampsConfig.defaultConfig()
let captureVC = JaakStampsViewController(config: config)
XCTAssertNotNil(captureVC)
// 3. Verificar delegado
captureVC.delegate = self
XCTAssertNotNil(captureVC.delegate)
// 4. Precargar modelo
let expectation = XCTestExpectation(description: "Model preload")
captureVC.preloadModel { success in
XCTAssertTrue(success)
expectation.fulfill()
}
wait(for: [expectation], timeout: 10.0)
// 5. Verificar configuración dinámica
captureVC.setMaskSize(90.0)
captureVC.setDebugMode(true)
print("✅ Test de integración completado")
}
📚 Referencia Completa del SDK
🔧 Métodos Principales
Método | Descripción | Ejemplo |
---|---|---|
JaakStampsViewController(config:) | Constructor del controlador | JaakStampsViewController(config: config) |
startCapture() | Inicia la captura | controller.startCapture() |
stopCapture() | Detiene la captura | controller.stopCapture() |
preloadModel(completion:) | Precarga modelo ML | controller.preloadModel { success in } |
resetComponent() | Reinicia el componente | controller.resetComponent() |
⚙️ Métodos de Configuración Dinámica
Método | Descripción | Rango/Valores |
---|---|---|
setMaskSize(_:) | Cambiar tamaño de máscara | 50.0 - 100.0 |
setAlignmentTolerance(_:) | Cambiar tolerancia de alineación | 5.0 - 50.0 |
setCropMargin(_:) | Cambiar margen de recorte | 0.0 - 100.0 |
setDebugMode(_:) | Activar/desactivar debug | true/false |
setPreferredCamera(_:) | Cambiar cámara preferida | .auto/.front/.back |
🔍 Métodos de Utilidad
Método | Descripción | Retorno |
---|---|---|
JAAKStampsSDK.isCompatible() | Verifica compatibilidad | Bool |
JAAKStampsSDK.getSystemRequirements() | Obtiene requisitos | [String: Any] |
JAAKStampsSDK.getCameraPermissionStatus() | Estado de permisos | AVAuthorizationStatus |
JAAKStampsSDK.getAvailableCameraDevices() | Dispositivos disponibles | [AVCaptureDevice] |
JAAKStampsSDK.requestCameraPermission(_:) | Solicita permisos | void |
📊 Configuraciones Disponibles
Configuración | Descripción | Valor por Defecto | Cuándo usar |
---|---|---|---|
defaultConfig() | Configuración estándar | Valores balanceados | Uso general |
debug | Modo debug | false | Desarrollo y debugging |
maskSize | Tamaño de máscara | 85.0 | Ajustar según tipo de documento |
alignmentTolerance | Tolerancia de alineación | 20.0 | Ajustar precisión vs velocidad |
autoCapture | Captura automática | true | Experiencia automatizada |
autoCaptureDelay | Delay de auto-captura | 1.5s | Dar tiempo al usuario |
useDocumentClassification | Clasificación de documentos | false | Identificar tipo de documento |
cropMargin | Margen de recorte | 0.0 | Incluir más contexto |
preferredCamera | Cámara preferida | .back | Calidad vs conveniencia |
📡 Estructura de Delegados
Delegados Básicos
jaakStampsDidBecomeReady(_:)
- SDK listojaakStamps(_:didCompleteCapture:)
- Captura completadajaakStamps(_:didFailWithError:)
- Error en capturajaakStamps(_:didUpdateStatus:)
- Actualización de estado
Delegados Avanzados
jaakStamps(_:didUpdateAlignment:)
- Actualización de alineaciónjaakStamps(_:didClassifyDocument:)
- Clasificación de documentojaakStamps(_:didChangeCamera:)
- Cambio de cámara
📊 Estructura de Respuesta Detallada
CapturedImages
struct CapturedImages {
let front: ImagePair // Imágenes frontales
let back: ImagePair // Imágenes traseras
let metadata: Metadata // Metadatos
}
ImagePair
struct ImagePair {
let fullFrame: UIImage? // Imagen completa del frame
let cropped: UIImage? // Imagen recortada por IA
}
Metadata
struct Metadata {
let totalImages: Int // Número total de imágenes
let processCompleted: Bool // Si el proceso se completó
let backCaptureSkipped: Bool // Si se omitió la captura trasera
let timestamp: Date // Marca de tiempo
let captureSteps: [CaptureStep] // Pasos completados
let documentType: DocumentType? // Tipo de documento detectado
let processingTime: TimeInterval // Tiempo de procesamiento
}
⚠️ Tipos de Error
enum JaakStampsError: Error {
case cameraPermissionDenied // Permiso denegado
case cameraNotAvailable // Cámara no disponible
case modelLoadingFailed(String) // Error cargando modelo
case captureSessionFailed(String) // Error en sesión
case processingFailed(String) // Error en procesamiento
case networkError(String) // Error de red
case invalidConfiguration(String) // Configuración inválida
case unknown(String) // Error desconocido
}
🚨 Solución de Problemas Avanzada
Problemas de Compatibilidad
Error: "SDK no compatible"
// ✅ Verificar antes de usar
if !JAAKStampsSDK.isCompatible() {
let requirements = JAAKStampsSDK.getSystemRequirements()
print("Requisitos no cumplidos: \(requirements)")
showCompatibilityError()
return
}
Error: "Modelo ML no se carga"
// ✅ Precargar explícitamente
captureController.preloadModel { success in
if success {
print("Modelo cargado exitosamente")
captureController.startCapture()
} else {
print("Error cargando modelo - reinicia la app")
self.showModelError()
}
}
Problemas de Rendimiento
Captura lenta en dispositivos antiguos
// ✅ Configuración optimizada para dispositivos lentos
var config = JaakStampsConfig()
config.maxFramesPerSecond = 15 // Reducir FPS
config.minConfidenceThreshold = 0.6 // Reducir umbral
config.maskSize = 70.0 // Reducir área de detección
Consumo excesivo de memoria
// ✅ Optimización de memoria
override func viewDidDisappear(_ animated: Bool) {
super.viewDidDisappear(animated)
// Limpiar recursos del SDK
jaakStampsController?.stopCapture()
jaakStampsController = nil
// Forzar liberación de memoria
DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) {
// Permitir que el sistema libere memoria
}
}
Problemas de Calidad de Imagen
Imágenes borrosas o de baja calidad
// ✅ Configuración para mejor calidad
var config = JaakStampsConfig()
config.autoCaptureDelay = 2.0 // Más tiempo para estabilizar
config.minConfidenceThreshold = 0.8 // Mayor confianza
config.cropMargin = 10.0 // Más margen de recorte
Detección inconsistente
// ✅ Ajustar parámetros de detección
captureController.setAlignmentTolerance(15.0) // Más estricto
captureController.setMaskSize(80.0) // Área más específica
📞 ¿Necesitas Ayuda?
🆘 Información para Soporte
Cuando contactes al soporte, incluye siempre:
Información del Dispositivo
// Agregar este código para obtener información de debug
private func generateDebugInfo() -> String {
let device = UIDevice.current
let isCompatible = JAAKStampsSDK.isCompatible()
let systemReqs = JAAKStampsSDK.getSystemRequirements()
let cameraStatus = JAAKStampsSDK.getCameraPermissionStatus()
return """
=== INFORMACIÓN DE DEBUG ===
Dispositivo: \(device.model)
iOS: \(device.systemVersion)
SDK Compatible: \(isCompatible)
Requisitos: \(systemReqs)
Permisos Cámara: \(cameraStatus)
Memoria disponible: \(ProcessInfo.processInfo.physicalMemory / 1024 / 1024) MB
"""
}
Logs de Error
- Screenshots de errores en consola Xcode
- Logs específicos del SDK (con debug activado)
- Pasos exactos para reproducir el problema
Configuración Utilizada
// Compartir la configuración usada
private func getConfigurationSummary() -> String {
guard let controller = jaakStampsController else { return "No inicializado" }
return """
=== CONFIGURACIÓN ACTUAL ===
Modo Debug: \(controller.isDebugModeEnabled)
Cámara Preferida: \(controller.getCurrentCameraPreference())
Configuración: \(controller.getCurrentConfiguration())
"""
}
📋 Contacto de Soporte
📧 Email: [email protected]
📱 Incluir siempre:
- Logs de iOS completos
- Información de dispositivo
- Configuración del SDK
- Versión del SDK utilizada
- Tipo de documento capturado
📖 Recursos Adicionales
¡Listo! 🎉
Has implementado exitosamente el JAAKStamps SDK con todas sus funcionalidades avanzadas. Tu aplicación iOS ahora puede:
✅ Capturar documentos automáticamente usando inteligencia artificial
✅ Detectar y alinear documentos en tiempo real
✅ Generar imágenes optimizadas (originales y recortadas)
✅ Clasificar tipos de documento automáticamente
✅ Manejar errores de forma robusta
✅ Configurar dinámicamente el comportamiento del SDK
✅ Optimizar rendimiento para diferentes dispositivos
Tu aplicación está lista para procesos KYC de nivel empresarial.
Updated 5 days ago