One Logo Pool Ball
wwwwwwwwwwwwwwwwwww

Data Scanner

Recognize live text and machine-readable codes with VisionKit

One.iOS.DataScannerViewController wraps VisionKit’s live data scanner: point the camera at text or barcodes and read what it recognizes, live. Reach for it for coupon codes, serial numbers, business cards, and ticket validation. For capturing document pages as images, see the document camera instead.

This needs an iOS build, supported physical hardware, and ios.infoPlist.NSCameraUsageDescription. Check the SDK’s isSupported and isAvailable before presenting; the simulator reports unsupported.

Usage

import { One } from 'one'
const Scanner = One.iOS.DataScannerViewController
if (Scanner.isSupported && Scanner.isAvailable) {
const scanner = new Scanner({
recognizedDataTypes: [Scanner.RecognizedDataType.text(), Scanner.RecognizedDataType.barcode()],
recognizesMultipleItems: true,
})
scanner.delegate = {
dataScanner: (sender, event) => {
if ('didTapOn' in event) {
sender.stopScanning()
void sender.dismiss()
}
},
}
await scanner.present()
scanner.startScanning()
for await (const items of scanner.recognizedItems) {
// consume each native batch in SDK reading order.
if (items.length > 0) break
}
scanner.stopScanning()
await scanner.dismiss()
}

Scanning starts explicitly after presentation. Batches stream through recognizedItems; breaking the loop cancels the subscription.

How it works

The initializer takes an options object for Swift’s labeled arguments. The data-type set is an array the native SDK dedupes. RecognizedDataType.text(languages, textContentType?) and RecognizedDataType.barcode(symbologies) mirror the SDK factories. Barcode symbologies use Vision raw string values such as QR. Text content types are date-time, email, flight number, street address, tracking number, telephone, URL, and currency. All configuration is readonly after initialization; every field except the data types is optional with SDK defaults.

recognizedItems is an async iterable of item arrays backed by the native async stream. Each iterator owns a subscription, and breaking a for await loop cancels it; consumers calling next directly must call return to cancel. It delivers complete ordered batches, including empty ones, without fabricating recognition or snapshotting delegate callbacks. stopScanning pauses recognition; iteration cancellation is separate.

Delegate labeled overloads adapt to dataScanner(sender, event) with tap, add, update, remove, or became-unavailable cases. Add, update, and remove events include every tracked item. dataScannerDidZoom preserves the separate SDK method. Unavailability preserves the unsupported and camera-restricted cases; operation rejection uses their error codes.

Recognized items are tagged objects: a text or barcode type, a UUID-string id, and bounds with four corner points. Text items carry a transcript; barcodes carry an optional payload string. Native Vision observation objects and the overlay container view are not bound; this is a scanner subset, not the complete Vision or view API.

present and dismiss adapt view controller ownership to the app’s current presenter and resolve after the transition. Dismissal stops scanning; a delegate event never dismisses. capturePhoto returns the same native image encoding subset as the document camera, with PNG and JPEG buffer output. regionOfInterest is a CGRect-shaped rectangle in view points; undefined clears it. Zoom factors use SDK units.

API reference

AvailabilityTypeDescription
isSupportedbooleanStatic SDK hardware support.
booleanStatic SDK hardware support.
isAvailablebooleanStatic SDK camera availability.
booleanStatic SDK camera availability.
supportedTextRecognitionLanguagesstring[]Static SDK language identifiers.
string[]Static SDK language identifiers.
Initializer configurationTypeDescription
recognizedDataTypesDataScannerRecognizedDataType[]Required text and barcode filters.
DataScannerRecognizedDataType[]Required text and barcode filters.
qualityLevelDataScannerQualityLevelbalanced by default; also fast or accurate.
DataScannerQualityLevelbalanced by default; also fast or accurate.
recognizesMultipleItemsbooleanDefaults to false.
booleanDefaults to false.
isHighFrameRateTrackingEnabledbooleanDefaults to true.
booleanDefaults to true.
isPinchToZoomEnabledbooleanDefaults to true.
booleanDefaults to true.
isGuidanceEnabledbooleanDefaults to true.
booleanDefaults to true.
isHighlightingEnabledbooleanDefaults to false.
booleanDefaults to false.

All configuration properties are readonly after initialization. Except for recognizedDataTypes, initializer fields are optional and use the defaults above.

ControllerTypeDescription
delegateDataScannerViewControllerDelegate | nullNative scanner events; defaults to null.
DataScannerViewControllerDelegate | nullNative scanner events; defaults to null.
regionOfInterestDataScannerRect | undefinedScan region in view points.
DataScannerRect | undefinedScan region in view points.
isScanningbooleanNative scanning state.
booleanNative scanning state.
minZoomFactornumberSDK minimum supported zoom.
numberSDK minimum supported zoom.
maxZoomFactornumberSDK maximum supported zoom.
numberSDK maximum supported zoom.
zoomFactornumberCurrent camera zoom.
numberCurrent camera zoom.
recognizedItemsAsyncIterable<RecognizedItem[]>Native async stream of complete batches.
AsyncIterable<RecognizedItem[]>Native async stream of complete batches.
present(animated?: boolean) => Promise<void>Presents the scanner; animation defaults to true.
(animated?: boolean) => Promise<void>Presents the scanner; animation defaults to true.
dismiss(animated?: boolean) => Promise<void>Stops scanning and dismisses; animation defaults to true.
(animated?: boolean) => Promise<void>Stops scanning and dismisses; animation defaults to true.
startScanning() => voidStarts native recognition or throws.
() => voidStarts native recognition or throws.
stopScanning() => voidStops recognition.
() => voidStops recognition.
capturePhoto() => Promise<UIImage>Captures a high-resolution native image.
() => Promise<UIImage>Captures a high-resolution native image.

Platform differences

iOSAndroidWeb
ScannerVisionKit liveThrowsThrows
TextLive transcriptNoneNone
CodesLive payloadsNoneNone
SupportPer deviceNeverNever

Limits and gotchas

Examples

Scan one QR code

import { One } from 'one'
async function scanQrCode(): Promise<string | null> {
const Scanner = One.iOS.DataScannerViewController
if (!Scanner.isSupported || !Scanner.isAvailable) return null
const scanner = new Scanner({
recognizedDataTypes: [Scanner.RecognizedDataType.barcode(['QR'])],
})
await scanner.present()
scanner.startScanning()
try {
for await (const items of scanner.recognizedItems) {
const code = items.find(
(item) => item.type === 'barcode' && item.payloadStringValue
)
if (code?.payloadStringValue) return code.payloadStringValue
}
} finally {
scanner.stopScanning()
await scanner.dismiss()
}
return null
}

Filtering to QR keeps unrelated barcodes out of the stream. The first payload wins; the finally stops and dismisses however the loop exits.

Read a tracking number aloud

import { One } from 'one'
async function readTrackingNumber(): Promise<string | null> {
const Scanner = One.iOS.DataScannerViewController
if (!Scanner.isSupported || !Scanner.isAvailable) return null
const scanner = new Scanner({
recognizedDataTypes: [
Scanner.RecognizedDataType.text([], 'shipmentTrackingNumber'),
],
})
await scanner.present()
scanner.startScanning()
try {
for await (const items of scanner.recognizedItems) {
const line = items.find((item) => item.type === 'text' && item.transcript)
if (line?.transcript) return line.transcript
}
} finally {
scanner.stopScanning()
await scanner.dismiss()
}
return null
}

The content type narrows recognition to tracking-number-shaped text, cutting false reads from surrounding print. The transcript arrives complete per item, never as fragments to stitch.

Edit this page on GitHub.