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. No necesitas conocimientos técnicos avanzados - solo sigue los pasos.
📋 Antes de Empezar - Lista de Verificación
Asegúrate de tener estos elementos listos:
- Xcode 12.0+ instalado
- Dispositivo iOS 13.0+ (físico, no simulador)
- CocoaPods configurado
- Conocimientos básicos de Swift/UIKit
- 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 | 10 min |
Paso 2 | Implementación básica del SDK | 20 min |
Paso 3 | Configurar permisos de cámara | 10 min |
Paso 4 | Manejo de respuestas y imágenes | 15 min |
Paso 5 | Probar captura de documentos | 10 min |
PASO 1: Configurar Proyecto y Dependencias
🎯 Objetivo
Configurar el entorno de desenvolvimento y añadir las dependencias necesarias del SDK.
✅ Requisitos Técnicos
Requisito | Versión | ¿Obligatorio? |
---|---|---|
Xcode | 12.0+ | Sí |
iOS | 13.0+ | Sí |
Swift | 5.5+ | Sí |
CocoaPods | Latest | Sí |
RAM | 2GB+ | Recomendado |
1.1 Configuración Podfile
platform :ios, '13.0'
use_frameworks!
target 'YourApp' do
# SDK JAAKStamps - Versión Beta
pod 'jaak-stamps', '>= 1.0.0-beta.1', '< 1.0.0-dev'
end
1.2 Instalación de Dependencias
# Instalar dependencias
pod install
# Abrir workspace (importante: no el .xcodeproj)
open YourApp.xcworkspace
1.3 Configuración de Build Settings
⚠️ IMPORTANTE: Para el correcto funcionamiento de la biblioteca, es necesario configurar el siguiente build setting:
User Script Sandboxing = NO
¿Cómo configurar?
- En Xcode, selecciona tu proyecto (no el target)
- Ve a Build Settings
- Busca "User Script Sandboxing" (puedes usar el campo de búsqueda)
- Cambia el valor a "No"
¿Por qué es necesario?
La biblioteca JAAKStamps utiliza scripts durante el proceso de build para:
- Copiar recursos necesarios (modelos ONNX, archivos de configuración)
- Configurar frameworks nativos
- Validar dependencias
El sandboxing de scripts puede interferir con estos procesos, causando errores de build o runtime. Deshabilitar esta opción permite que la biblioteca funcione correctamente.
Nota de Seguridad: Esta configuración es específica para el proceso de build y no afecta la seguridad de la aplicación final.
1.4 Configuración de Frameworks Necesarios
En tu proyecto Xcode, verifica que estos frameworks estén linkedos:
- UIKit - Interfaz de usuario
- AVFoundation - Acceso a cámara
- CoreML - Procesamiento de IA
- Vision - Análisis de imágenes
- Foundation - Funcionalidades base
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
import UIKit
import JAAKStamps
class DocumentCaptureViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
setupDocumentCapture()
}
private func setupDocumentCapture() {
let config = JaakStampsConfig.defaultConfig()
let captureController = JaakStampsViewController(config: config)
captureController.delegate = self
captureController.modalPresentationStyle = .fullScreen
present(captureController, animated: true) {
captureController.startCapture()
}
}
}
extension DocumentCaptureViewController: JaakStampsDelegate {
func jaakStampsDidBecomeReady(_ controller: JaakStampsViewController) {
print("SDK listo para captura")
}
func jaakStamps(_ controller: JaakStampsViewController, didCompleteCapture images: CapturedImages) {
print("Captura completada")
if let frontImage = images.front.fullFrame {
processImage(frontImage, type: "front_full")
}
if let frontCrop = images.front.cropped {
processImage(frontCrop, type: "front_crop")
}
if let backImage = images.back.fullFrame {
processImage(backImage, type: "back_full")
}
if let backCrop = images.back.cropped {
processImage(backCrop, type: "back_crop")
}
controller.dismiss(animated: true)
}
func jaakStamps(_ controller: JaakStampsViewController, didFailWithError error: JaakStampsError) {
print("Error: \(error.localizedDescription)")
controller.dismiss(animated: true)
}
func jaakStamps(_ controller: JaakStampsViewController, didUpdateStatus status: ComponentStatus) {
// Método requerido - implementación básica
print("Status actualizado: \(status)")
}
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()
}
}
2.2 Configuración SwiftUI (Opcional)
import SwiftUI
import JAAKStamps
struct DocumentCaptureView: UIViewControllerRepresentable {
@Binding var isPresented: Bool
@Binding var capturedImages: CapturedImages?
func makeUIViewController(context: Context) -> JaakStampsViewController {
let config = JaakStampsConfig.defaultConfig()
let controller = JaakStampsViewController(config: config)
controller.delegate = context.coordinator
return controller
}
func updateUIViewController(_ uiViewController: JaakStampsViewController, context: Context) {
if isPresented && !uiViewController.isProcessCompleted() {
uiViewController.startCapture()
}
}
func makeCoordinator() -> Coordinator {
return Coordinator(self)
}
class Coordinator: NSObject, JaakStampsDelegate {
let parent: DocumentCaptureView
init(_ parent: DocumentCaptureView) {
self.parent = parent
}
func jaakStampsDidBecomeReady(_ controller: JaakStampsViewController) {
print("SDK listo")
}
func jaakStamps(_ controller: JaakStampsViewController, didCompleteCapture images: CapturedImages) {
DispatchQueue.main.async {
self.parent.capturedImages = images
self.parent.isPresented = false
}
}
func jaakStamps(_ controller: JaakStampsViewController, didFailWithError error: JaakStampsError) {
DispatchQueue.main.async {
self.parent.isPresented = false
}
}
func jaakStamps(_ controller: JaakStampsViewController, didUpdateStatus status: ComponentStatus) {
// Método requerido - implementación básica
print("Status actualizado: \(status)")
}
}
}
2.3 Uso en SwiftUI
struct ContentView: View {
@State private var showCapture = false
@State private var capturedImages: CapturedImages?
var body: some View {
VStack {
Button("Capturar Documento") {
showCapture = true
}
.padding()
if capturedImages != nil {
Text("✅ Documento capturado")
.foregroundColor(.green)
}
}
.fullScreenCover(isPresented: $showCapture) {
DocumentCaptureView(
isPresented: $showCapture,
capturedImages: $capturedImages
)
}
}
}
PASO 3: Configurar Permisos de Cámara
🎯 Objetivo
Configurar correctamente los permisos de cámara requeridos por iOS.
3.1 Info.plist
<key>NSCameraUsageDescription</key>
<string>Esta aplicación necesita acceso a la cámara para capturar documentos</string>
3.2 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.3 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() {
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 y Imágenes
🎯 Objetivo
Implementar el manejo completo de las imágenes capturadas y conversión necesaria.
4.1 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)")
// 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,
"platform": "ios"
]
]
}
private func sendToBackend(_ payload: [String: Any]) {
print("Enviando a backend: \(payload.keys)")
// Implementar llamada a tu API
}
4.2 Manejo Avanzado 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)
default:
handleGenericError(error.localizedDescription)
}
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. Reinicia la aplicación e intenta nuevamente."
)
}
private func handleCaptureError(_ message: String) {
showAlert(
title: "Error de Captura",
message: "Error durante la captura del documento. Intenta nuevamente."
)
}
private func handleProcessingError(_ message: String) {
showAlert(
title: "Error de Procesamiento",
message: "Error al procesar la imagen. Verifica que el documento esté bien iluminado."
)
}
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: Probar Captura de Documentos
🎯 Objetivo
Verificar que todas las funcionalidades del SDK funcionan correctamente.
✅ Lista de Verificación
Elemento | ✅ | Descripción |
---|---|---|
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 |
🔍 Proceso de Prueba
Paso A: 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
Paso B: 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 C: Probar Documento Doble Cara
- Capturar frente del documento
- Seguir instrucciones para voltear
- Capturar reverso automáticamente
- Verificar 4 imágenes en logs
- Confirmar procesamiento exitoso
🛠️ Debugging y Troubleshooting
Verificar Compatibilidad del Dispositivo
private func checkDeviceCompatibility() {
print("=== VERIFICANDO COMPATIBILIDAD ===")
let isCompatible = JAAKStampsSDK.isCompatible()
print("SDK compatible: \(isCompatible)")
let coreMLAvailable = JAAKStampsSDK.isCoreMLAvailable()
print("Core ML disponible: \(coreMLAvailable)")
let cameraStatus = PermissionsManager.checkCameraPermission()
print("Estado cámara: \(cameraStatus)")
let systemReqs = JAAKStampsSDK.getSystemRequirements()
print("Requisitos del sistema: \(systemReqs)")
}
private func enableDebugMode() {
var config = JaakStampsConfig.defaultConfig()
config.debug = true
// Usar configuración con debug habilitado
let captureController = JaakStampsViewController(config: config)
}
Problemas Comunes
Problema | Solución |
---|---|
"SDK no compatible" | ✅ Verificar iOS 13.0+ y Core ML disponible |
"Cámara no inicia" | ✅ Verificar permisos y que no esté ocupada |
"Modelo no carga" | ✅ Reiniciar app y verificar memoria disponible |
"Captura no funciona" | ✅ Probar en dispositivo físico, no simulador |
"User Script Sandboxing" | ✅ Cambiar a "No" en Build Settings del proyecto |
📚 Referencia Completa
🔧 Métodos del SDK
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 } |
📡 Configuraciones Disponibles
Configuración | Descripción | Cuándo usar |
---|---|---|
defaultConfig() | Configuración estándar | Uso general |
highAccuracyConfig() | Alta precisión | Documentos complejos |
highSpeedConfig() | Alta velocidad | Captura rápida |
📊 Estructura de Respuesta
Imagen | Descripción | Cuándo se genera |
---|---|---|
front.fullFrame | Imagen completa del frente | Siempre |
front.cropped | Frente recortado por IA | Cuando detecta documento |
back.fullFrame | Imagen completa del reverso | Solo si documento requiere reverso |
back.cropped | Reverso recortado por IA | Cuando detecta reverso |
⚙️ Utilidades
Método | Descripción | Ejemplo |
---|---|---|
JAAKStampsSDK.isCompatible() | Verifica compatibilidad | JAAKStampsSDK.isCompatible() |
JAAKStampsSDK.isCoreMLAvailable() | Verifica Core ML | JAAKStampsSDK.isCoreMLAvailable() |
🚨 Solución de Problemas
Problema: "Error de modelo ML"
// ✅ Verificar antes de usar
if !JAAKStampsSDK.isCoreMLAvailable() {
showAlert(title: "Dispositivo Incompatible",
message: "Este dispositivo no soporta Core ML")
return
}
// Precargar modelo explícitamente
captureController.preloadModel { success in
if success {
captureController.startCapture()
} else {
self.showAlert(title: "Error", message: "No se pudo cargar el modelo")
}
}
Problema: "Cámara no funciona en simulador"
// ✅ Solo funciona en dispositivo físico
#if targetEnvironment(simulator)
showAlert(title: "Simulador",
message: "La captura de documentos requiere un dispositivo físico")
return
#endif
Problema: "Permisos constantemente denegados"
// ✅ Verificar configuración del sistema
private func checkPermissionStatus() {
let status = AVCaptureDevice.authorizationStatus(for: .video)
if status == .denied {
showAlert(
title: "Permisos Requeridos",
message: "Ve a Configuración > Privacidad > Cámara y habilita el acceso para esta app"
) {
PermissionsManager.openSettings()
}
}
}
Problema: "User Script Sandboxing Error"
// ✅ Verificar configuración del proyecto
private func checkBuildSettings() {
#if DEBUG
print("⚠️ Verificar Build Settings:")
print("1. Seleccionar PROYECTO (no target)")
print("2. Build Settings > User Script Sandboxing = NO")
print("3. Clean Build Folder y recompilar")
#endif
}
📞 ¿Necesitas Ayuda?
🆘 Información para soporte
- Descripción del problema: Qué intentas hacer vs qué sucede
- Logs de Xcode: Screenshots de errores en consola
- Información del dispositivo: Modelo iOS, versión, memoria
- Configuración Podfile: Versiones de dependencias
- Tipo de documento: INE, pasaporte, licencia, etc.
📋 Contacto de Soporte
📧 Email: [email protected]
📱 Incluir siempre: Logs iOS, configuración, versión SDK
¡Listo! 🎉
Has implementado exitosamente el JAAKStamps SDK en tu aplicación iOS. Tu app ahora puede capturar documentos automáticamente usando inteligencia artificial, generando imágenes optimizadas para procesos KYC.
Updated 22 days ago