Note: This guide covers scanning readable codes 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 readable codes on documents. This enables you to get both a clean image of the document as well as structured data representing the readable codes 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 = .readableCode
configuration.structuredDataReadableCodeTypes = [.ean13]
configuration.multiPage = false
configuration.skipPostProcessingScreen = true
val configuration = ScanConfiguration().apply {
structuredData = EnumSet.of(StructuredData.READABLE_CODE)
structuredDataReadableCodeTypes = EnumSet.of(ReadableCodeType.EAN_13)
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)
}
// Start scan flow
ScanFlow.scanWithConfiguration(activity, configuration)
// Handle result in onActivityResult
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data)
try {
val result = ScanFlow.getScanResultFromActivityResult(data)
handleResult(result)
} catch (e: Exception) {
handleError(e)
}
}
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.structuredDataResult else {
print("No structured data result")
return
}
// Check if readable codes have been detected
guard let readableCodes = structuredResult.readableCodes else {
print("No readable codes detected")
return
}
for readableCode in readableCodes {
print("Type: \(readableCode.type)")
print("Value: \(readableCode.value)")
}
}
fun handleResult(result: ScanResult) {
// 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 readable codes have been detected
val readableCodes = structuredResult.readableCodes ?: run {
Log.d("Scanner", "No readable codes detected")
return
}
for (readableCode in readableCodes) {
Log.d("Scanner", "Type: ${readableCode.type}")
Log.d("Scanner", "Value: ${readableCode.value}")
}
}
It’s important to handle errors properly. There are two important cases:
func handleError(_ error: Error) {
if (error as NSError).domain == GSKScanFlowErrorDomain && (error as NSError).code == GSKScanFlowError.userCancellation.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: Exception) {
// 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:
© 2025 The Grizzly Labs. All rights reserved.