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.
import { One } from 'one'
const Scanner = One.iOS.DataScannerViewControllerif (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.
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.
| Availability | Type | Description |
|---|---|---|
isSupportedbooleanStatic SDK hardware support. | boolean | Static SDK hardware support. |
isAvailablebooleanStatic SDK camera availability. | boolean | Static SDK camera availability. |
supportedTextRecognitionLanguagesstring[]Static SDK language identifiers. | string[] | Static SDK language identifiers. |
| Initializer configuration | Type | Description |
|---|---|---|
recognizedDataTypesDataScannerRecognizedDataType[]Required text and barcode filters. | DataScannerRecognizedDataType[] | Required text and barcode filters. |
qualityLevelDataScannerQualityLevelbalanced by default; also fast or accurate. | DataScannerQualityLevel | balanced by default; also fast or accurate. |
recognizesMultipleItemsbooleanDefaults to false. | boolean | Defaults to false. |
isHighFrameRateTrackingEnabledbooleanDefaults to true. | boolean | Defaults to true. |
isPinchToZoomEnabledbooleanDefaults to true. | boolean | Defaults to true. |
isGuidanceEnabledbooleanDefaults to true. | boolean | Defaults to true. |
isHighlightingEnabledbooleanDefaults to false. | boolean | Defaults to false. |
All configuration properties are readonly after initialization. Except for
recognizedDataTypes, initializer fields are optional and use the defaults
above.
| Controller | Type | Description |
|---|---|---|
delegateDataScannerViewControllerDelegate | nullNative scanner events; defaults to null. | DataScannerViewControllerDelegate | null | Native scanner events; defaults to null. |
regionOfInterestDataScannerRect | undefinedScan region in view points. | DataScannerRect | undefined | Scan region in view points. |
isScanningbooleanNative scanning state. | boolean | Native scanning state. |
minZoomFactornumberSDK minimum supported zoom. | number | SDK minimum supported zoom. |
maxZoomFactornumberSDK maximum supported zoom. | number | SDK maximum supported zoom. |
zoomFactornumberCurrent camera zoom. | number | Current 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. | () => void | Starts native recognition or throws. |
stopScanning() => voidStops recognition. | () => void | Stops recognition. |
capturePhoto() => Promise<UIImage>Captures a high-resolution native image. | () => Promise<UIImage> | Captures a high-resolution native image. |
| iOS | Android | Web | |
|---|---|---|---|
| Scanner | VisionKit live | Throws | Throws |
| Text | Live transcript | None | None |
| Codes | Live payloads | None | None |
| Support | Per device | Never | Never |
startScanning.for await cancels, but a
manually advanced iterator needs return to release its subscription.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.
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.