Note: This guide covers extracting barcodes from documents. For real-time, continuous barcode scanning (e.g., warehouse scanning, inventory management), see the Barcode Scanning Quick Start guide.
As part of its structured data capabilities, the Genius Scan SDK is able to detect barcodes on documents. This enables you to get both a clean image of the document as well as structured data representing the barcodes present on the document. For instance, this may let you send a scanned document to the proper backend depending on a barcode present on the document.
We recommend using the Genius Scan SDK if you are building a barcode or QR code scanning feature as part of a document acquisition process.
The following readable code formats are supported:
In this guide, we will write a very simple barcode scanning app. Writing this app should take about 10 minutes.
Add the Genius Scan SDK as a dependency to your project by following the platform-specific guide:
You will instantiate a scan flow with a specific configuration:
let configuration = GSKScanFlowConfiguration()
configuration.structuredData = [.barcode]
configuration.structuredDataBarcodeTypes = [.ean13]
configuration.multiPage = false
configuration.skipPostProcessingScreen = true
val configuration = ScanFlowConfiguration().apply {
structuredData = EnumSet.of(ScanFlowConfiguration.StructuredData.BARCODE)
structuredDataBarcodeTypes = EnumSet.of(Barcode.Type.EAN13)
multiPage = false
skipPostProcessingScreen = true
}
You can now start the scan flow.
// Keep a strong reference on ScanFlow
let scanFlow = GSKScanFlow(configuration: configuration)
do {
let result = try await scanFlow.resultByStarting(fromViewController: viewController)
handleResult(result)
} catch {
handleError(error)
}
val scanLauncher = registerForActivityResult(ScanActivity.Contract()) { output ->
when (output) {
is FlowOutput.Success -> handleResult(output.result)
is FlowOutput.Error -> handleError(output.error)
}
}
scanLauncher.launch(configuration)
We can now write the result handler. Since this is a demo, we will just output the extracted data but in your real application you will likely display the data to the user for validation, and then serialize it to send it to your backend.
func handleResult(_ result: GSKScanFlowResult) {
// We only consider the first scan since we are in single-page mode
guard let scan = result.scans.first else {
print("Unexpected error")
return
}
// Check if there is structured data
guard let structuredResult = scan.structuredData else {
print("No structured data result")
return
}
// Check if barcodes have been detected
let barcodes = structuredResult.barcodes
if barcodes.isEmpty {
print("No barcodes detected")
return
}
for barcode in barcodes {
print("Type: \(barcode.type)")
print("Value: \(barcode.value)")
}
}
fun handleResult(result: ScanFlowResult) {
// We only consider the first scan since we are in single-page mode
val scan = result.scans?.firstOrNull() ?: run {
Log.e("Scanner", "Unexpected error")
return
}
// Check if there is structured data
val structuredResult = scan.structuredDataResult ?: run {
Log.d("Scanner", "No structured data result")
return
}
// Check if barcodes have been detected
val barcodes = structuredResult.barcodes
if (barcodes.isEmpty()) {
Log.d("Scanner", "No barcodes detected")
return
}
for (barcode in barcodes) {
Log.d("Scanner", "Type: ${barcode.type}")
Log.d("Scanner", "Value: ${barcode.value}")
}
}
It’s important to handle errors properly. There are two important cases:
func handleError(_ error: Error) {
let nsError = error as NSError
if nsError.domain == GSKScanFlowErrorDomain && nsError.code == GSKScanFlowErrorCode.cancellation.rawValue {
// User cancelled - do nothing
} else {
// Show a UIAlertController with error.localizedDescription
let alert = UIAlertController(title: "Error", message: error.localizedDescription, preferredStyle: .alert)
alert.addAction(UIAlertAction(title: "OK", style: .default))
viewController.present(alert, animated: true)
}
}
fun handleError(error: ScanFlowError) {
if (error.code == ScanFlowErrorCode.CANCELLATION) {
return
}
// Show an AlertDialog with error message
AlertDialog.Builder(activity)
.setTitle("Error")
.setMessage(error.message)
.setPositiveButton("OK") { dialog, _ -> dialog.dismiss() }
.show()
}
You can now build and run the application on an actual device to test the barcode scanning:
Start with a free trial license to test the SDK, or contact us directly for a custom quote tailored to your needs.
© 2026 The Grizzly Labs. All rights reserved.