Documentation

Integration Documentation(CustomView Mode)

This document explains how to use the core UI component AAIIQACameraWrapperView

Overview

AAIIQACameraWrapperView is a UIView component that provides essential scanning and photo capture capabilities. You can directly integrate it into your own page, giving you complete control over UI layout and interaction.

Usage Steps

  1. Before using, please refer to the integration documentation to incorporate the SDK into your project, as this component is provided by the "AAIGlobalIQASDK" module.
    Note you don't need to integrate the AAIGlobalIQAUI module, as AAIIQACameraWrapperView is a standalone component that does not depend on the default UI module, which can reduce the package size.

  2. Import the AAIGlobalIQASDK module where you need to use AAIIQACameraWrapperView.

    import AAIGlobalIQASDK
  3. Init SDK, then set the license.

    AAIGlobalIQASDK.initSDK()
    let result = AAIGlobalIQASDK.setLicenseAndCheck(demoLicenseContent)
    if result == "SUCCESS" {
        // License is valid. We can start using AAIIQACameraWrapperView
        // ...
    } else {
        if result == "LICENSE_EXPIRE" {
            print("LICENSE_EXPIRE: please call your server's api to generate a new license")
        } else if result == "APPLICATION_ID_NOT_MATCH" {
            print("APPLICATION_ID_NOT_MATCH: please bind your app's bundle identifier on our cms website, then recall your server's api to generate a new license")
        } else {
            print("\(result)")
        }
    }
  4. Add AAIIQACameraWrapperView to your view hierarchy, and configure it as needed.

  5. Use the startScanMode(with:delegate:) method to begin scanning mode, or the startPhotoMode(with:delegate:) method to start photo capture mode.

  6. Implement the AAIIQACameraWrapperViewAuthDelegate protocol and AAIIQACameraWrapperViewScanModeDelegate or AAIIQACameraWrapperViewPhotoModeDelegate protocol to handle scan or photo capture callback events.

Example

Below is a simple example demonstrating how to use AAIIQACameraWrapperView for scanning and taking photos. You can find similar sample code in the demo project(CameraViewDemo1/MyCameraDemo1ViewController.swift), which you may also reference for implementation.

import AAIGlobalIQASDK

class YourViewController: UIViewController {
    
    private var iqaView: AAIIQACameraWrapperView?

    private lazy var scanConfig: AAIIQACameraWrapperViewScanConfig = {
        let config = AAIIQACameraWrapperViewScanConfig()
        config.region = "ID"
        config.cardType = .idCard
        config.cardSide = .front
        config.detectionTimeoutInterval = 20
        return config
    }()
    
    private lazy var photoConfig: AAIIQACameraWrapperViewPhotoConfig = {
        let config = AAIIQACameraWrapperViewPhotoConfig()
        config.region = "ID"
        config.cardType = .idCard
        config.cardSide = .front
        return config
    }()

    override func viewDidLoad() {
        super.viewDidLoad()
        
        // Initialize the iqaView
        let iqaView = AAIIQACameraWrapperView()
        // Add the iqaView to the main view
        self.view.addSubview(iqaView)
        // Set constraints or frame for the iqaView
        iqaView.translatesAutoresizingMaskIntoConstraints = false
        NSLayoutConstraint.activate([
            iqaView.topAnchor.constraint(equalTo: self.view.topAnchor, constant: 100),
            iqaView.widthAnchor.constraint(equalTo:self.view.widthAnchor, multiplier: 0.9),
            // Set the height to maintain a 16:10 aspect ratio (Recommended)
            iqaView.heightAnchor.constraint(equalTo:iqaView.widthAnchor, multiplier: 0.625),
            iqaView.centerXAnchor.constraint(equalTo: self.view.centerXAnchor)
        ])

    }

    override func willTransition(to newCollection: UITraitCollection, with coordinator: UIViewControllerTransitionCoordinator) {
        // Notify the iqaView about the orientation change
        iqaView?.willTransition(to: UIDevice.current.orientation)
        super.willTransition(to: newCollection, with: coordinator)
    }

    @objc func tapStartScanModeBtnAction() {
        // 1. Update the scanning configuration if needed
        /*
        // e.g., scanConfig.region = "ID"
        scanConfig.region = "id"
        scanConfig.cardType = .idCard
        scanConfig.cardSide = .front
        scanConfig.detectionTimeoutInterval = 20
        */
        // 2. Start the scanning mode
        iqaView?.startScanMode(with: scanConfig, delegate: self)
    }

    @objc func tapStartPhotoModeBtnAction() {
        // 1. Update the photo configuration if needed
            /*
            // e.g., photoConfig.region = "ID"
            photoConfig.region = "id"
            photoConfig.cardType = .idCard
            photoConfig.cardSide = .front
            */
            // 2. Start the photo mode
            iqaView?.startPhotoMode(with: photoConfig, delegate: self)
            
            // During photo mode, you can:
            // - Take a photo by calling iqaView?.capturePhoto()
            // - Retake a photo by calling iqaView?.discardCapturedPhoto()
            // - Confirm and use the captured photo by calling iqaView?.useCapturedPhoto()
    }

    @objc func tapTakePhotoBtnAction() {
        iqaView?.capturePhoto()
    }
    
    @objc func tapRetakePhotoBtnAction() {
        iqaView?.discardCapturedPhoto()
    }

    func process(result: AAIGlobalIQAResultProtocol) {
        // We recommend you to save the eventId for future issue tracking.
        let eventId = result.eventId
        print("eventId: \(String(describing: eventId))")
        
        if result.success {
            // The final output captured card image in base64 format.
            // Note that this image data is suitable for uploading to the server as it has been
            // appropriately compressed. You can also obtain this image by calling server side api
            // 'openapi/face-identity/image-quality-check/v1/query'
            let cardImgBase64String = result.cardImgBase64String
            
            // The raw output captured card image without any compression.
            // Note that this image is generally used for preview purposes.
            // If you want to upload the image to the server, you should use the
            // `cardImgBase64String` property.
            let cardImg = result.cardImg
            
            // The identifier string that corresponds to the captured card image,
            // which can be used to retrieve the image by calling the server side api
            // 'openapi/face-identity/image-quality-check/v1/query'.
            let idvid = result.idvid
        } else {
            let errorCode = result.errorCode
            let errorMsg = result.errorMsg
            print("errorCode: \(errorCode ?? ""), errorMsg: \(String(describing: errorMsg ?? ""))")
        }
    }
}

// MARK: - AAIIQACameraWrapperViewScanModeDelegate
// Implement the delegate methods to handle scanning events
extension YourViewController: AAIIQACameraWrapperViewScanModeDelegate {
    
    func iqaOnScanStart() {
        print("[callback][iqaOnScanStart]")
    }
    
    func iqa(onScanWarnCodeChanged warnCode: AAIIQAWarnCode) {
        print("[callback][onScanWarn][\(warnCode.rawValue)]")
        var stateKey: String?
        var msg: String?
        switch warnCode {
        case .noCard:
            stateKey = "no_card"
            msg = "No document is detected"
        case .tooSmallCard:
            stateKey = "too_small_card"
            msg = "Document is too small"
        case .edgeCross:
            stateKey = "iqa_min_gap_ratio"
            msg = "The document is incomplete\nplease make sure the document in the center with the edges aligned"
        case .cardPoorQuality:
            stateKey = "iqa_card_poor_quality"
            msg = "Document is too dim/blurred/overexposed"
        case .good:
            stateKey = "hold_phone"
            msg = "Please hold the phone steadily"
        case .hasOccluded:
            stateKey = "card_has_occluded"
            msg = "Please keep your ID card unobstructed"
        case .unspecified:
            let _ = "";
        @unknown default:
            fatalError("Unknown AAIIQAWarnCode!")
        }
        
        print("[callback][onScanWarn][\(warnCode.rawValue)] stateKey: \(String(describing: stateKey)), msg: \(String(describing: msg))")
    }
    
    func iqa(onScanSuccess rawImage: UIImage?) {
        print("[callback][onScanSuccess]")
        // Note that you should obtain the original scanned image from the `iqa(onScanModeComplete:)` method
    }
    
    func iqa(onScanRemainingSecondsChanged remainSeconds: Int, totalSeconds: Int) {
        print("[callback][onDetectionRemainTimeChanged: \(remainSeconds)/\(totalSeconds)]")
    }
    
    func iqa(onScanModeComplete result: AAIGlobalIQAResultProtocol) {
        print("[callback][onScanModeComplete]")

        process(result: result)

        if result.errorCode == "SCAN_TIMEOUT" {
            // Demo: Enter photoMode
            self.iqaView?.startPhotoMode(with: photoConfig, delegate: self)
            
            /*
            // Demo: Retry scan
            self.iqaView?.startScanMode(with: scanConfig, delegate: self)
             */
            return;
        } else {
            // Stop the iqaView after scan mode is complete
            self.iqaView?.stopRunning()
        }
    }
}

// MARK: - AAIIQACameraWrapperViewPhotoModeDelegate
// Implement the delegate methods to handle photo capture events
extension YourViewController: AAIIQACameraWrapperViewPhotoModeDelegate {
    func iqaOnPhotoModeStart() {
        print("[callback][iqaOnPhotoModeStart]")
    }
    
    func iqa(onCapturePhotoDone capturedImage: UIImage?, previewImage: UIImage) {
        print("[callback][onCapturePhotoDone]")
        
        // Show the preview image
        // You can display the preview image in an UIImageView or any other UI component
    }
    
    func iqa(onPhotoModeComplete result: AAIGlobalIQAResultProtocol) {
        print("[callback][onPhotoModeComplete]")

        process(result: result)

        // Stop the iqaView after photo mode is complete
        self.iqaView?.stopRunning()
    }
}

// MARK: - AAIIQACameraWrapperViewAuthDelegate
// Implement the delegate methods to handle authentication events
extension YourViewController: AAIIQACameraWrapperViewAuthDelegate {
    func iqaOnAuthCheckStart() {
        print("[callback][iqaOnAuthCheckStart]")
        // Generally, you should show a loading view here
    }
    
    func iqa(onAuthCheckFinish error: Error?) {
        print("[callback][onAuthCheckFinish]")
        // Dismiss the loading view here
    }
    
    func iqaOnUploadDataStart() {
        print("[callback][iqaOnUploadDataStart]")
        // Show a loading view to indicate that data is being uploaded
    }
    
    func iqa(onUploadDataFinish error: Error?) {
        print("[callback][onUploadDataFinish][\(String(describing: error))]")
        // Dismiss the loading view here
    }
}

Image Warn Code

During scanning mode, the iqa(onScanWarnCodeChanged:) delegate method will be called to provide real-time feedback on the quality of the scanned image. The following table lists the possible warning codes and their meanings:

Warn Code (Swift)Warn Code (Objective-C)MeaningSuggested prompts
.noCardAAIIQAWarnCodeNoCardNo card detectedNo document is detected
.tooSmallCardAAIIQAWarnCodeTooSmallCardCard area is too smallDocument is too small
.edgeCrossAAIIQAWarnCodeEdgeCrossCard edges are incompleteThe document is incomplete\nplease make sure the document in the center with the edges aligned
.cardPoorQualityAAIIQAWarnCodeCardPoorQualityLow card quality: Dim/Blurred/OverexposedDocument is too dim/blurred/overexposed
.hasOccludedAAIIQAWarnCodeHasOccludedCard area is occludedPlease keep your ID card unobstructed
.goodAAIIQAWarnCodeGoodCard quality is acceptablePlease hold the phone steady
.unspecifiedAAIIQAWarnCodeUnspecified--

Error Code

See Error Codes Table for details about error codes.