Complete Manual for document capture in iOS applications

Estimated time: 45-60 minutes for complete configuration


What will you learn from this manual?

This manual will teach you how to implement and use JAAKStamps SDK for automated capture of identity documents in native iOS applications. No advanced technical skills required - Just follow the steps.


Before Start - Checks list

Be sure to have these elements ready:

  • Xcode 12.0 + installed
  • iOS 13.0 + device (physical, non-simulator)
  • Cocoa
  • Basic information about Swift / UIKit
  • Access to a functional camera
  • Test ID

Contents Index

SectionWhat will you do?Time
Step 1Configure Project and Units10 min
Step 2Basic implementation of SDK20 min
Step 3Configure Camera Permissions10 min
Step 4Management of responses and images15 min
Step 5Test document capture10 min

Step 1: Configure Project and Units

Objective

Configure the development environment and add the necessary SDK.

Technical requirements

RequirementVersionMandatory?
Xcode12.0+Yes
iOS13.0+Yes
Swift5.5+Yes
CocoaLastYes
RAM2GB +Recommended

1.1 Podfile configuration

platform :ios, '13.0'
use_frameworks!

target 'YourApp' do
  # SDK JAAKStamps - Beta Version
  pod 'jaak-stamps', '>= 1.0.0-beta.1', '< 1.0.0-dev'
end

1.2 Unit installation

# Instalar dependencias
pod install

# Open workspace (important: not .xcodeproj)
open YourApp.xcworkspace

1.3 Building Settings

Important

For the proper functioning of the library, you have to set up the following building setting: Using Sandboxing Script = NO

How to set up?

  1. At Xcode, select your project (not target)
  2. Go to Build Settings
  3. Search "Using Sandboxing Script" (you can use the search field)
  4. Switch Value To "No"

Why's that necessary?

The JAAKStamps library uses scripts during the build process to:

  • Copy Resource Requires (ONNX models, configuration files)
  • Configure native frameworks
  • Validate dependencies

Script sandboxing can interfere with these processes, leading to built or run-time errors. Unabling this option allows the library to work properly.

Security note: That configuration is specific to the build process and does not affect final application security.

1.4 Frameworks Requirement

In your Xcode project, check that these frameworks are linked:

  • UIKit - User interface
  • AVFoundation - Access to camera
  • Korea - AI processing
  • Vision - Image analysis
  • Foundation - Base functions

Step 2: Basic implementation of SDK

Objective

Create JAAKStamps core implementation SDK with an automated capture.

2.1 Basic Viewer

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 ready for capture")
    }

    func jaakStamps(_ controller: JaakStampsViewController, didCompleteCapture images: CapturedImages) {
        print("Capture completed")

        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.locatedDescription)")
        controller.dismiss(animated: true)
    }

    func jaakStamps(_ controller: JaakStampsViewController, didUpdateStatus status: ComponentStatus) {
        // Required method - basic implementation
        print("Status actualizado: \(status)")
    }

    private func processImage(_ image: UIImage, type: String) {
        print("Reprocessing image:\ (type)")
        // Convert to base64 if necessary
        // let base64 = image.jpegData(compressionQuality: 0.8)?.base64EncodedString()
    }
}

2.2 SwiftIU (Optional) configuration

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 That's it.")
        }

        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) {
            // Required method - basic implementation
            print("Status actualizado: \(status)")
        }
    }
}

2.3 Use in SwiftIU

struct ContentView: View {
    @State private var showCapture = false
    @State private var capturedImages: CapturedImages?

    var body: some View {
        VStack {
            Button("Capture document") {
                showCapture = true
            }
            .padding()

            if capturedImages != nil {
                Text("Captured document")
                    .foregroundColor(.green)
            }
        }
        .fullScreenCover(isPresented: $showCapture) {
            DocumentCaptureView(
                isPresented: $showCapture,
                capturedImages: $capturedImages
            )
        }
    }
}

Passage 3: Configure Camera Permissions

Objective

Configure camera permissions required by iOS correctly.

3.1 Info.pist

<key>NSCameraUsageDescription</key>
<string>Esta aplicación necesita acceso a la cámara para capturar documentos</string>

3.2 Management of Code Permises

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 Verification Before Capture

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: "Camera permits",
        message: "Camera access required to capture document",
        preferredStyle: .alert
    )

    alert.addAction(UIAlertAction(title: "Configure", style: .default) { _ in
        PermissionsManager.openSettings()
    })

    alert.addAction(UIAlertAction(title: "Cancelar", style: .cancel))

    present(alert, animated: true)
}

Step 4: Reg and Images Management

Objective

Implement complete management of the captured images and necessary conversion.

4.1 Full Image Processing

func jaakStamps(_ controller: JaakStampsViewController, didCompleteCapture images: CapturedImages) {
    let documentImages = DocumentImages()

    // Full front processing
    if let frontFull = images.front.fullFrame {
        documentImages.frontOriginal = convertToBase64(frontFull)
        print("Front Original: \(documentImages.frontOriginal?.count ?? 0) chars")
    }

    // To process cut face
    if let frontCrop = images.front.cropped {
        documentImages.frontCrop = convertToBase64(frontCrop)
        print("Front Crop: \(documentImages.frontCrop?.count ?? 0) chars")
    }

    // Reg complete (if available)
    if let backFull = images.back.fullFrame {
        documentImages.backOriginal = convertToBase64(backFull)
        print("Back Original: \(documentImages.backOriginal?.count ?? 0) chars")
    }

    // Procure cut back (if available)
    if let backCrop = images.back.cropped {
        documentImages.backCrop = convertToBase64(backCrop)
        print("Back Crop: \(documentImages.backCrop?.count ?? 0) chars")
    }

    // Determine document type
    let documentType = images.metadata.backCaptureSkipped ? "single_sided" : "double_sided"

    // To process complete document
    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("_")
    print("Tipo: \(type)")
    print("Total images:\ (metadata)")
    print("Complete process:\ (methata.ProcesCompleted)")

    // Validate that we have at least the front image
    guard images.frontOriginal != nil || images.frontCrop != nil else {
        print("Error: No valid face images found")
        return
    }

    // Create payload for backend
    let payload = createDocumentPayload(images, type: type, metadata: metadata)

    // Send to backend or process locally
    sendToBackend(payload)

    print("Reprocessing document")
}

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)")
    // Implement call to your API
}

4.2 Advanced Errors Management

func jaakStamps(_ controller: JaakStampsViewController, didFailWithError error: JaakStampsError) {
    print("Reaping error:\ (error.locatedDescription)")

    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: "Camera permits",
        message: "Access to the camera is required to capture documents. Go to Setup to enable them."
    ) {
        PermissionsManager.openSettings()
    }
}

private func handleCameraError() {
    showAlert(
        title: "Camera error",
        message: "The camera was unable to be accessed. Check that it isn't being used by another application."
    )
}

private func handleMLError(_ message: String) {
    showAlert(
        title: "Processing Error",
        message: "The model of AI could not be loaded. Reboot the application and try again."
    )
}

private func handleCaptureError(_ message: String) {
    showAlert(
        title: "Capture error",
        message: "Error during document capture. Try again."
    )
}

private func handleProcessingError(_ message: String) {
    showAlert(
        title: "Processing Error",
        message: "Failed to process the image. Check that the document is well lit."
    )
}

private func handleGenericError(_ message: String) {
    showAlert(
        title: "Error",
        message: "Unexpected error happened:\ (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: "Configure", style: .default) { _ in
            action()
        })
        alert.addAction(UIAlertAction(title: "Cancelar", style: .cancel))
    } else {
        alert.addAction(UIAlertAction(title: "Entendido", style: .default))
    }

    present(alert, animated: true)
}

Step 5: Test document capture

Objective

Check that all functionalities of SDK They work properly.

checklist

ElementStateDescription
Camera permits☐App requests and gets permission
Automatic detection☐AI detects document automatically
Imaging☐Original and face / back crop
Basal conversion64☐All images become
Errors management☐Mistakes are handled correctly

Test process

Step A: Test Permissions

  1. Install Application as a physical device
  2. Open Application first time
  3. Check request automatic permits
  4. Grant permits for camera

Step B: Test Simple Capture

  1. Press Button Capture
  2. Position document within the framework
  3. To expect automatic detection (Green frame)
  4. Check capture automatic
  5. Review login to confirm processing

Step C: Test Double face document

  1. Capture front document
  2. Following instructions To turn
  3. Capture Reverse automatically
  4. Check 4 images at login
  5. Confirm processing success

Debugging and Troublehooting

Check Unit Compatibility

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("Camera status:\ (cameraStatus)")

    let systemReqs = JAAKStampsSDK.getSystemRequirements()
    print("System requirements:\ (systemReqs)")
}

private func enableDebugMode() {
    var config = JaakStampsConfig.defaultConfig()
    config.debug = true

    // Use configuration with activated debug
    let captureController = JaakStampsViewController(config: config)
}

Common problems

ProblemResolution
"SDK Non-compatible "Check iOS 13.0 + and Core ML available
"Camera doesn't start."Check permits and that are not busy
"Non-Loading Model"Reboot app and check available memory
"Capture doesn't work."Test with physical device, non-simulator
"Using Sandboxing Script"Switch to "No" at Build Settings project

Full reference

Methods SDK

MethodDescriptionExample
JaakStampsViewController (config:)Driver's builderJaakStampsViewController(config: config)
stardature ()Initiates capturecontroller.startCapture()
stopCapture ()Stops capturecontroller.stopCapture()
preLoadModel (completion:)ML modelcontroller.preloadModel { success in }

Available

ConfigureDescriptionWhen to use
defaultConfig ()Standard configurationGeneral
highAccuracyConfig ()High precisionComplex documents
highSpeedConfig ()High SpeedRapid capture

Response structure

ImageDescriptionWhen it's generated
front.fullFrameFull face imageAlways
front.croppedFront cut by AIWhen detecting document
back.fullFameFull back imageOnly if document requires back
back.cropedReverse cut by AIWhen you get back

Fits

MethodDescriptionExample
JAAKStampsSDK.isCompatible ()Verify compatibilityJAAKStampsSDK.isCompatible()
JAAKStampCheck Core MLJAAKStampsSDK.isCoreMLAvailable()

Problem solving

Problem: "ML model error"

// Check before use
if !JAAKStampsSDK.isCoreMLAvailable() {
    showAlert(title: "Dispositivo Incompatible",
              message: "That device doesn't support Core ML")
    return
}

// Recharging model explicitly
captureController.preloadModel { success in
    if success {
        captureController.startCapture()
    } else {
        self.showAlert(title: "Error", message: "Can't load model")
    }
}

Problem: "Camera doesn't work in simulator"

// It only works on a physical device
#if targetEnvironment(simulator)
    showAlert(title: "Simulador",
              message: "The capture of documents requires a physical device")
    return
#endif

Problem: "Rejected permits"

// Check system configuration
private func checkPermissionStatus() {
    let status = AVCaptureDevice.authorizationStatus(for: .video)

    if status == .denied {
        showAlert(
            title: "Permisos Requeridos",
            message: "Go to Setup > Privacy > Camera and enabling access for this app"
        ) {
            PermissionsManager.openSettings()
        }
    }
}

Problem: "Using Sandboxing Failed Script"

// Check project configuration
private func checkBuildSettings() {
    #if DEBUG
    print("Check Build Settings:")
    print("1. Select PROJECT (do not target)")
    print("2. Build Settings > User Script Sandboxing = NO")
    print("3. Clean Build Folder and Remodel")
    #endif
}

Do you need some help?

Support information

  • Problem description: What are you trying to do with what's going on?
  • Xcode's logs: Screenshots of console errors
  • Unit information: IOS model, version, memory
  • Podfile Setup: Unit versions
  • Type of document: INE, passport, license, etc.

Support Contact

Email: support Always include: IOS logo, configuration, version SDK


You have successfully implemented JAAKStamps SDK your iOS application. Your app can now capture documents automatically using artificial intelligence, generating optimization images for processes KYC.