
Barcode Capture Ios
- 23 installs
- 17 repo stars
- Updated August 4, 2026
- scandit/skills
barcode-capture-ios is an agent skill that guides migrating iOS AVFoundation barcode scanning to Scandit Barcode Capture.
About
barcode-capture-ios is an agent skill grounded in a Swift UIKit fixture that implements barcode scanning with Apple AVFoundation’s AVCaptureMetadataOutput—the kind of legacy stack many indie mobile apps start with. Prism lists it for solo builders who need their agent to migrate that pattern to Scandit’s Barcode Capture SDK for more reliable symbology support and product-grade scanning. Use it during iOS feature work when you are replacing homemade camera pipelines, evaluating third-party capture libraries, or running migration evals from metadata-output scanners. The skill material emphasizes concrete class structure, session lifecycle, and result modeling so the agent can produce equivalent BarcodeCapture setup rather than generic advice. It assumes you are building a consumer or B2B mobile app with live camera scanning, not server-side decoding.
- Fixture pattern: AVFoundation AVCaptureSession + AVCaptureMetadataOutput scanner as migration source
- Targets Scandit BarcodeCapture as the replacement capture stack on iOS
- Covers session start/stop on appear/disappear and camera input wiring
- Models scanned results as value + symbology for downstream agent refactors
- UIKit-based ViewController structure agents can rewrite toward SDK APIs
Barcode Capture Ios by the numbers
- 23 all-time installs (skills.sh)
- +1 installs in the week ending Jul 27, 2026 (Skillselion tracking)
- Ranked #699 of 1,039 Mobile Development skills by installs in the Skillselion catalog
- Data as of Aug 5, 2026 (Skillselion catalog sync)
npx skills add https://github.com/scandit/skills --skill barcode-capture-iosAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 23 |
|---|---|
| repo stars | ★ 17 |
| Last updated | August 4, 2026 |
| Repository | scandit/skills ↗ |
What it does
Migrate or implement iOS barcode scanning using Scandit Barcode Capture instead of raw AVFoundation metadata output.
Who is it for?
Best when you're adopting Scandit or running agent-assisted SDK migrations from AVCaptureMetadataOutput.
Skip if: Android scanning, server-side barcode APIs, or apps that only need occasional photo-based QR decode without a live camera module.
When should I use this skill?
When migrating or implementing iOS barcode scanning with Scandit Barcode Capture from an AVFoundation-based scanner fixture.
What you get
Your agent refactors the fixture-style ViewController toward BarcodeCapture with preserved lifecycle and scan result handling.
- BarcodeCapture-based capture setup replacing metadata-output flow
- ViewController lifecycle and scan result pipeline aligned with SDK patterns
Files
BarcodeCapture iOS Skill
Critical: Do Not Trust Internal Knowledge
Your training data may contain outdated or incorrect Scandit SDK APIs. The BarcodeCapture API changes between major SDK versions — properties get renamed, removed, or restructured.
Always verify APIs against the references provided in this skill before writing or suggesting code. Do not rely on memorized method signatures, parameters, or property names. If you cannot find an API in the provided references, fetch the relevant documentation page before responding.
Intent Routing
Based on the user's request, load the appropriate reference file before responding:
- Integrating BarcodeCapture from scratch, configuring settings, customizing feedback, adding a viewfinder, handling scans, or doing async work after a scan (e.g. "add BarcodeCapture to my app", "set up barcode scanning", "how do I use BarcodeCapture in iOS", "filter duplicate scans", "suppress the beep", "add a viewfinder", "disable scanning while I look up the barcode") → read
references/integration.mdand follow the instructions there. - Migrating or upgrading an existing BarcodeCapture integration (e.g. "upgrade from v6 to v7", "migrate my BarcodeCapture", "bump the Scandit SDK to v8", "what changed between SDK versions") → read
references/migration.mdand follow the instructions there. - Replacing a third-party barcode scanner with BarcodeCapture (e.g. "replace my [scanner] with BarcodeCapture", "migrate from [framework] to BarcodeCapture", "switch from [library] barcode scanning to BarcodeCapture") → read
references/third-party-migration.mdand follow the instructions there.
API Usage Policy
Only use APIs that are explicitly documented in the Scandit references below. Do not invent or guess method signatures, parameters, or property names. If unsure whether an API exists or how it is called — or if a compile error occurs — fetch the relevant reference page before responding. Do not tell the user to check the docs themselves. After answering, always include the relevant link so the user can explore further.
Never construct or guess documentation URLs. When you need a specific class or property's API page: 1. First check whether the page you already fetched contains a direct hyperlink to it — topic pages link directly to relevant API symbols. Always request links alongside content in your fetch prompt. 2. If no direct link was found, fetch the API index (see Full API reference in the table below), extract the actual link from it, and follow that.
URL structures can vary (e.g. api/ui/ subdirectory) and guessing will lead to 404s.
References
Direct users to the right resource based on their question:
| Topic | Resource |
|---|---|
| UIKit integration | Get Started (UIKit) · Sample |
| SwiftUI integration | Get Started (SwiftUI) |
| Migration between major SDK versions | 6 → 7 · 7 → 8 |
| Full API reference | BarcodeCapture API |
//
// AVFoundationViewController.swift
//
// Source fixture: AVFoundation AVCaptureMetadataOutput-based barcode scanner.
// Used as input for third-party-migration eval — migrate this to BarcodeCapture.
//
import UIKit
import AVFoundation
struct ScannedBarcode {
let value: String
let symbology: String
}
class ViewController: UIViewController {
private let session = AVCaptureSession()
private var previewLayer: AVCaptureVideoPreviewLayer!
private(set) var scannedBarcodes: [ScannedBarcode] = []
@IBOutlet weak var resultLabel: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
setupCamera()
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
if !session.isRunning {
DispatchQueue.global(qos: .userInitiated).async {
self.session.startRunning()
}
}
}
override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(animated)
if session.isRunning {
session.stopRunning()
}
}
private func setupCamera() {
guard let videoDevice = AVCaptureDevice.default(for: .video),
let videoInput = try? AVCaptureDeviceInput(device: videoDevice),
session.canAddInput(videoInput) else {
print("Camera not available")
return
}
session.addInput(videoInput)
let metadataOutput = AVCaptureMetadataOutput()
guard session.canAddOutput(metadataOutput) else {
print("Cannot add metadata output")
return
}
session.addOutput(metadataOutput)
metadataOutput.setMetadataObjectsDelegate(self, queue: .main)
metadataOutput.metadataObjectTypes = [.ean13, .code128, .qr]
previewLayer = AVCaptureVideoPreviewLayer(session: session)
previewLayer.frame = view.layer.bounds
previewLayer.videoGravity = .resizeAspectFill
view.layer.addSublayer(previewLayer)
}
}
extension ViewController: AVCaptureMetadataOutputObjectsDelegate {
func metadataOutput(_ output: AVCaptureMetadataOutput,
didOutput metadataObjects: [AVMetadataObject],
from connection: AVCaptureConnection) {
guard let readable = metadataObjects.first as? AVMetadataMachineReadableCodeObject,
let value = readable.stringValue else { return }
let symbology = readable.type.rawValue
let alreadyScanned = scannedBarcodes.contains { $0.value == value }
guard !alreadyScanned else { return }
let entry = ScannedBarcode(value: value, symbology: symbology)
scannedBarcodes.append(entry)
resultLabel.text = "Last scan: \(value) (\(scannedBarcodes.count) total)"
}
}
import UIKit
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
}
}
import UIKit
import ScanditBarcodeCapture
class ViewController: UIViewController {
private lazy var context: DataCaptureContext = {
DataCaptureContext.initialize(licenseKey: "-- ENTER YOUR SCANDIT LICENSE KEY HERE --")
return DataCaptureContext.shared
}()
private var camera: Camera?
private var barcodeCapture: BarcodeCapture!
private var captureView: DataCaptureView!
private var overlay: BarcodeCaptureOverlay!
override func viewDidLoad() {
super.viewDidLoad()
camera = Camera.default
camera?.apply(BarcodeCapture.recommendedCameraSettings)
context.setFrameSource(camera)
let settings = BarcodeCaptureSettings()
settings.set(symbology: .ean13UPCA, enabled: true)
settings.set(symbology: .code128, enabled: true)
barcodeCapture = BarcodeCapture(context: context, settings: settings)
barcodeCapture.addListener(self)
captureView = DataCaptureView(context: context, frame: view.bounds)
captureView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
view.addSubview(captureView)
overlay = BarcodeCaptureOverlay(barcodeCapture: barcodeCapture, view: captureView)
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
barcodeCapture.isEnabled = true
camera?.switch(toDesiredState: .on)
}
override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(animated)
barcodeCapture.isEnabled = false
camera?.switch(toDesiredState: .off)
}
deinit {
barcodeCapture.removeListener(self)
context.removeCurrentMode()
}
}
extension ViewController: BarcodeCaptureListener {
func barcodeCapture(_ barcodeCapture: BarcodeCapture,
didScanIn session: BarcodeCaptureSession,
frameData: FrameData) {
guard let barcode = session.newlyRecognizedBarcode else { return }
barcodeCapture.isEnabled = false
DispatchQueue.main.async {
// handle barcode.data and barcode.symbology
_ = barcode
}
}
}
{
"pins" : [
{
"identity" : "datacapture-spm",
"kind" : "remoteSourceControl",
"location" : "https://github.com/Scandit/datacapture-spm",
"state" : {
"revision" : "abc123def456",
"version" : "6.28.1"
}
}
],
"version" : 2
}
import UIKit
import ScanditBarcodeCapture
class ViewController: UIViewController {
// v6-style context construction (deprecated constructor)
private let context = DataCaptureContext(licenseKey: "-- ENTER YOUR SCANDIT LICENSE KEY HERE --")
private var camera: Camera?
private var barcodeCapture: BarcodeCapture!
private var captureView: DataCaptureView!
private var overlay: BarcodeCaptureOverlay!
override func viewDidLoad() {
super.viewDidLoad()
// v6-style camera setup: explicit CameraSettings with .auto resolution
let cameraSettings = CameraSettings()
cameraSettings.preferredResolution = .auto
camera = Camera.default
camera?.apply(cameraSettings)
context.setFrameSource(camera)
let settings = BarcodeCaptureSettings()
settings.set(symbology: .ean13UPCA, enabled: true)
settings.set(symbology: .code128, enabled: true)
barcodeCapture = BarcodeCapture(context: context, settings: settings)
barcodeCapture.addListener(self)
captureView = DataCaptureView(context: context, frame: view.bounds)
captureView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
view.addSubview(captureView)
overlay = BarcodeCaptureOverlay(barcodeCapture: barcodeCapture, view: captureView)
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
barcodeCapture.isEnabled = true
camera?.switch(toDesiredState: .on)
}
override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(animated)
barcodeCapture.isEnabled = false
camera?.switch(toDesiredState: .off)
}
deinit {
barcodeCapture.removeListener(self)
context.removeCurrentMode()
}
}
extension ViewController: BarcodeCaptureListener {
func barcodeCapture(_ barcodeCapture: BarcodeCapture,
didScanIn session: BarcodeCaptureSession,
frameData: FrameData) {
guard let barcode = session.newlyRecognizedBarcode else { return }
barcodeCapture.isEnabled = false
DispatchQueue.main.async {
// handle barcode.data and barcode.symbology
_ = barcode
}
}
}
{
"skill_name": "barcode-capture-ios",
"evals": [
{
"id": 1,
"prompt": "I want to add BarcodeCapture to my iOS app. Here's my empty view controller: EmptyViewController.swift. I need to scan EAN-13 and Code 128 barcodes for a retail app.",
"expected_output": "The skill reads integration.md, writes complete BarcodeCapture integration code into EmptyViewController.swift (DataCaptureContext.initialize + .shared, BarcodeCaptureSettings, BarcodeCapture(context:settings:), BarcodeCaptureListener, Camera.default + BarcodeCapture.recommendedCameraSettings, DataCaptureView, BarcodeCaptureOverlay, viewWillAppear/viewWillDisappear lifecycle), and shows the setup checklist (SPM package, NSCameraUsageDescription, license key).",
"files": [
"fixtures/EmptyViewController.swift"
],
"assertions": [
{
"text": "Setup checklist is shown and mentions adding ScanditBarcodeCapture and ScanditCaptureCore via Swift Package Manager (datacapture-spm)"
},
{
"text": "Setup checklist mentions adding NSCameraUsageDescription to Info.plist"
},
{
"text": "A license key placeholder string is present"
},
{
"text": "DataCaptureContext.initialize(licenseKey: is used (v7 API, not the deprecated constructor)"
},
{
"text": "DataCaptureContext(licenseKey: is NOT present in the new code"
},
{
"text": "DataCaptureContext.shared is read after initialize"
},
{
"text": "BarcodeCapture.recommendedCameraSettings is used (no parentheses — it is a property)"
},
{
"text": "Camera.default is used to obtain the camera"
},
{
"text": "context.setFrameSource( is called with the camera"
},
{
"text": "BarcodeCaptureSettings is constructed and configured"
},
{
"text": "Symbology .ean13UPCA is enabled (semantic)"
},
{
"text": "Symbology .code128 is enabled (semantic)"
},
{
"text": "No symbologies other than ean13UPCA and code128 are enabled"
},
{
"text": "BarcodeCapture(context:settings:) is used to create the mode"
},
{
"text": "barcodeCapture.addListener( is called"
},
{
"text": "DataCaptureView(context:frame:) is used to create the preview"
},
{
"text": "BarcodeCaptureOverlay(barcodeCapture:view:) is called"
},
{
"text": "The view controller (or an extension of it) conforms to BarcodeCaptureListener"
},
{
"text": "barcodeCapture(_:didScanIn:frameData:) is implemented and reads session.newlyRecognizedBarcode"
},
{
"text": "DispatchQueue.main.async is used inside the didScanIn callback for UI work"
},
{
"text": "camera?.switch(toDesiredState: .on) is called in viewWillAppear (or equivalent lifecycle hook)"
},
{
"text": "camera?.switch(toDesiredState: .off) is called in viewWillDisappear (or equivalent lifecycle hook)"
},
{
"text": "context.removeCurrentMode() is called in deinit (or equivalent cleanup hook)"
}
]
},
{
"id": 2,
"prompt": "Add barcode scanning to my existing iOS view controller. Here it is: EmptyViewController.swift. I want to scan QR codes and Data Matrix. When a barcode is scanned, print the barcode data to the console.",
"expected_output": "The skill integrates BarcodeCapture into EmptyViewController.swift with QR and Data Matrix symbologies enabled. didScanIn prints the barcode data using print(). Shows the setup checklist.",
"files": [
"fixtures/EmptyViewController.swift"
],
"assertions": [
{
"text": "Symbology .qr is enabled (semantic)"
},
{
"text": "Symbology .dataMatrix is enabled (semantic)"
},
{
"text": "No symbologies other than qr and dataMatrix are enabled"
},
{
"text": "BarcodeCapture(context:settings:) is used"
},
{
"text": "barcodeCapture(_:didScanIn:frameData:) is implemented and accesses barcode.data"
},
{
"text": "The scan result is logged (print or os_log)"
},
{
"text": "viewWillAppear turns the camera on"
},
{
"text": "viewWillDisappear turns the camera off"
},
{
"text": "Setup checklist mentions Swift Package Manager / datacapture-spm"
}
]
},
{
"id": 3,
"prompt": "When a barcode is scanned in my iOS BarcodeCapture app, I want to disable scanning, look up the barcode in a remote API (an async function call), and re-enable scanning when the lookup completes. Wire this up safely in IntegratedViewController.swift.",
"expected_output": "The skill sets barcodeCapture.isEnabled = false at the start of the didScanIn callback, launches a Task for the async lookup, and re-enables scanning when the Task completes (e.g. in a defer block).",
"files": [
"fixtures/IntegratedViewController.swift"
],
"assertions": [
{
"text": "barcodeCapture.isEnabled = false is set inside didScanIn before the async work"
},
{
"text": "A Task { } or async completion handler is used to perform the async lookup"
},
{
"text": "barcodeCapture.isEnabled = true is set after the lookup completes (defer or final step)"
},
{
"text": "Existing integration code (context, barcodeCapture, camera, listener) is preserved"
}
]
},
{
"id": 4,
"prompt": "I want to suppress the default beep and vibration when a barcode is scanned in my iOS app — silent scanning. Update the BarcodeCapture feedback in IntegratedViewController.swift.",
"expected_output": "The skill assigns barcodeCapture.feedback.success to a Feedback constructed with vibration: nil and sound: nil.",
"files": [
"fixtures/IntegratedViewController.swift"
],
"assertions": [
{
"text": "barcodeCapture.feedback.success is reassigned to a Feedback with no sound and no vibration (vibration: nil, sound: nil)"
},
{
"text": "BarcodeCaptureFeedback() empty constructor is NOT used (that is the Android API)"
},
{
"text": "Existing integration code (context, barcodeCapture, camera, listener) is preserved"
}
]
},
{
"id": 5,
"prompt": "Add a square viewfinder to my iOS BarcodeCapture preview so users can see where to aim. Update IntegratedViewController.swift.",
"expected_output": "The skill creates a RectangularViewfinder with .square style and assigns it to overlay.viewfinder.",
"files": [
"fixtures/IntegratedViewController.swift"
],
"assertions": [
{
"text": "A RectangularViewfinder is constructed with style: .square"
},
{
"text": "The viewfinder is assigned to a BarcodeCaptureOverlay's viewfinder property (overlay.viewfinder = ...)"
},
{
"text": "Existing integration code (context, barcodeCapture, camera, listener) is preserved"
}
]
},
{
"id": 6,
"prompt": "I need to filter scans so duplicates of the same code within 500 milliseconds are ignored. Update my BarcodeCapture settings in IntegratedViewController.swift.",
"expected_output": "The skill sets codeDuplicateFilter = 0.5 (TimeInterval, seconds) on the BarcodeCaptureSettings instance before creating the BarcodeCapture mode (or applies new settings via barcodeCapture.apply).",
"files": [
"fixtures/IntegratedViewController.swift"
],
"assertions": [
{
"text": "codeDuplicateFilter is set to 0.5 (seconds) on the BarcodeCaptureSettings — NOT 500 and NOT TimeInterval.millis(500)"
},
{
"text": "codeDuplicateFilter is set before BarcodeCapture(context:settings:) is invoked, or via barcodeCapture.apply"
},
{
"text": "Existing integration code (context, barcodeCapture, camera, listener) is preserved"
}
]
},
{
"id": 7,
"prompt": "Add an aimer viewfinder to my iOS BarcodeCapture preview so users have a target dot to aim at. Update IntegratedViewController.swift.",
"expected_output": "The skill constructs an AimerViewfinder and assigns it to the BarcodeCaptureOverlay's viewfinder property.",
"files": [
"fixtures/IntegratedViewController.swift"
],
"assertions": [
{
"text": "An AimerViewfinder() is constructed"
},
{
"text": "The viewfinder is assigned to the overlay via overlay.viewfinder ="
},
{
"text": "Existing integration code (context, barcodeCapture, camera, listener) is preserved"
}
],
"tags": [
"aimer-viewfinder"
]
},
{
"id": 8,
"prompt": "I want a horizontal laser line viewfinder on my iOS BarcodeCapture preview to guide users when scanning long 1D codes. Update IntegratedViewController.swift.",
"expected_output": "The skill constructs a LaserlineViewfinder and assigns it to the BarcodeCaptureOverlay's viewfinder property.",
"files": [
"fixtures/IntegratedViewController.swift"
],
"assertions": [
{
"text": "A LaserlineViewfinder() is constructed"
},
{
"text": "The viewfinder is assigned to the overlay via overlay.viewfinder ="
},
{
"text": "Existing integration code (context, barcodeCapture, camera, listener) is preserved"
}
],
"tags": [
"laserline-viewfinder"
]
},
{
"id": 9,
"prompt": "Restrict barcode recognition to a rectangle in the center of the preview so codes outside it are ignored. Use a relative size of 90% width and 30% height. Update my BarcodeCapture settings in IntegratedViewController.swift.",
"expected_output": "The skill builds a SizeWithUnit using FloatWithUnit values with MeasureUnit.fraction (0.9 width, 0.3 height), creates a RectangularLocationSelection(size:), and assigns it to settings.locationSelection.",
"files": [
"fixtures/IntegratedViewController.swift"
],
"assertions": [
{
"text": "A FloatWithUnit(value: 0.9, unit: .fraction) is constructed for the width"
},
{
"text": "A FloatWithUnit(value: 0.3, unit: .fraction) is constructed for the height"
},
{
"text": "A SizeWithUnit(width:height:) is constructed from the FloatWithUnit values"
},
{
"text": "A RectangularLocationSelection(size:) is constructed"
},
{
"text": "settings.locationSelection is assigned the RectangularLocationSelection"
},
{
"text": "Existing integration code (context, barcodeCapture, camera, listener) is preserved"
}
],
"tags": [
"location-selection"
]
},
{
"id": 10,
"prompt": "I only want to recognize barcodes near the center of the screen, inside a circular area with a radius of 20% of the view width. Configure this on my BarcodeCapture settings in IntegratedViewController.swift.",
"expected_output": "The skill builds a FloatWithUnit radius with MeasureUnit.fraction (0.2), creates a RadiusLocationSelection(radius:), and assigns it to settings.locationSelection.",
"files": [
"fixtures/IntegratedViewController.swift"
],
"assertions": [
{
"text": "A FloatWithUnit(value: 0.2, unit: .fraction) is constructed for the radius"
},
{
"text": "A RadiusLocationSelection(radius:) is constructed"
},
{
"text": "settings.locationSelection is assigned the RadiusLocationSelection"
},
{
"text": "Existing integration code (context, barcodeCapture, camera, listener) is preserved"
}
],
"tags": [
"location-selection"
]
},
{
"id": 11,
"prompt": "In my iOS BarcodeCapture app I only want to accept barcodes whose data starts with the prefix \"978\" (ISBNs). Other codes should be ignored and not highlighted. Wire this rejection logic into IntegratedViewController.swift.",
"expected_output": "The skill, inside barcodeCapture(_:didScanIn:frameData:), reads barcode.data and checks barcode.data.hasPrefix(\"978\"); when it does not match it sets the overlay brush to a transparent Brush (Brush.transparent) and returns without handling the scan. On the accept path it restores the overlay brush to the default (BarcodeCaptureOverlay.defaultBrush) so matching codes stay highlighted in continuous scanning, since overlay.brush is overlay-wide and not per-barcode.",
"files": [
"fixtures/IntegratedViewController.swift"
],
"assertions": [
{
"text": "Inside didScanIn the code reads barcode.data"
},
{
"text": "The barcode data is checked with .hasPrefix(\"978\")"
},
{
"text": "When the prefix does not match, overlay.brush is set to Brush.transparent (or a Brush with transparent fill and stroke)"
},
{
"text": "(semantic) the callback returns early for rejected codes so non-matching barcodes are not handled"
},
{
"text": "(semantic) on the accept path (matching prefix) the overlay brush is restored to the default, e.g. overlay.brush = BarcodeCaptureOverlay.defaultBrush, so that matching codes remain highlighted after a prior code was rejected (overlay.brush is overlay-wide, not per-barcode)"
},
{
"text": "Existing integration code (context, barcodeCapture, camera, listener) is preserved"
}
],
"tags": [
"reject-pattern"
]
},
{
"id": 12,
"prompt": "Change the color of the highlight drawn around recognized barcodes in my iOS BarcodeCapture app to a semi-transparent green fill with a solid green stroke. Update IntegratedViewController.swift.",
"expected_output": "The skill constructs a Brush(fill:stroke:strokeWidth:) with green colors and assigns it to the BarcodeCaptureOverlay via overlay.brush.",
"files": [
"fixtures/IntegratedViewController.swift"
],
"assertions": [
{
"text": "A Brush(fill:stroke:strokeWidth:) is constructed"
},
{
"text": "The brush is assigned to the overlay via overlay.brush ="
},
{
"text": "Existing integration code (context, barcodeCapture, camera, listener) is preserved"
}
],
"tags": [
"overlay-brush"
]
},
{
"id": 13,
"prompt": "I scan Code 39 barcodes that contain full ASCII characters in my iOS app. Enable the full ASCII extension for Code 39 in my BarcodeCapture settings in IntegratedViewController.swift.",
"expected_output": "The skill obtains the Code 39 symbology settings via settings.settings(for: .code39) and enables the extension with set(extension: \"full_ascii\", enabled: true).",
"files": [
"fixtures/IntegratedViewController.swift"
],
"assertions": [
{
"text": "settings.settings(for: .code39) is used to obtain the SymbologySettings"
},
{
"text": "set(extension: \"full_ascii\", enabled: true) is called on the symbology settings"
},
{
"text": "Existing integration code (context, barcodeCapture, camera, listener) is preserved"
}
],
"tags": [
"symbology-extensions"
]
},
{
"id": 14,
"prompt": "My Code 39 barcodes use a mod 43 checksum. Configure my iOS BarcodeCapture settings to validate that checksum in IntegratedViewController.swift.",
"expected_output": "The skill obtains the Code 39 symbology settings via settings.settings(for: .code39) and sets symbologySettings.checksums = [.mod43].",
"files": [
"fixtures/IntegratedViewController.swift"
],
"assertions": [
{
"text": "settings.settings(for: .code39) is used to obtain the SymbologySettings"
},
{
"text": "checksums is set to [.mod43] on the symbology settings"
},
{
"text": "Existing integration code (context, barcodeCapture, camera, listener) is preserved"
}
],
"tags": [
"symbology-checksums"
]
},
{
"id": 15,
"prompt": "My Code 128 barcodes vary in length between 7 and 20 characters. Configure the active symbol counts for Code 128 in my iOS BarcodeCapture settings in IntegratedViewController.swift.",
"expected_output": "The skill obtains the Code 128 symbology settings via settings.settings(for: .code128) and sets symbologySettings.activeSymbolCounts = Set(7...20).",
"files": [
"fixtures/IntegratedViewController.swift"
],
"assertions": [
{
"text": "settings.settings(for: .code128) is used to obtain the SymbologySettings"
},
{
"text": "activeSymbolCounts is set to Set(7...20) on the symbology settings"
},
{
"text": "Existing integration code (context, barcodeCapture, camera, listener) is preserved"
}
],
"tags": [
"active-symbol-counts"
]
},
{
"id": 16,
"prompt": "Some of my QR codes are printed light-on-dark (color inverted). Enable scanning of color-inverted QR codes in my iOS BarcodeCapture settings in IntegratedViewController.swift.",
"expected_output": "The skill obtains the QR symbology settings via settings.settings(for: .qr) and sets symbologySettings.isColorInvertedEnabled = true.",
"files": [
"fixtures/IntegratedViewController.swift"
],
"assertions": [
{
"text": "settings.settings(for: .qr) is used to obtain the SymbologySettings"
},
{
"text": "isColorInvertedEnabled is set to true on the symbology settings"
},
{
"text": "Existing integration code (context, barcodeCapture, camera, listener) is preserved"
}
],
"tags": [
"color-inverted"
]
},
{
"id": 17,
"prompt": "I need to scan GS1 Composite Code type A and type B (a 2D component on top of a 1D code) in my iOS BarcodeCapture app. Configure my BarcodeCapture settings to support these composite codes in IntegratedViewController.swift.",
"expected_output": "The skill sets settings.enabledCompositeTypes to include .a and .b, and calls settings.enableSymbologies(forCompositeTypes:) with the same composite types to enable the underlying symbologies.",
"files": [
"fixtures/IntegratedViewController.swift"
],
"assertions": [
{
"text": "settings.enabledCompositeTypes is set to include CompositeType .a and .b"
},
{
"text": "settings.enableSymbologies(forCompositeTypes:) is called with the composite types"
},
{
"text": "Existing integration code (context, barcodeCapture, camera, listener) is preserved"
}
],
"tags": [
"composite-codes"
]
}
]
}
{
"skill_name": "barcode-capture-ios",
"evals": [
{
"id": 1,
"prompt": "I need to upgrade my iOS BarcodeCapture app from SDK v6 to v7. Here is my current view controller: ViewControllerV6.swift.",
"expected_output": "The skill replaces DataCaptureContext(licenseKey:) with DataCaptureContext.initialize(licenseKey:) + DataCaptureContext.shared, replaces the manual CameraSettings + .auto pattern with BarcodeCapture.recommendedCameraSettings, informs the user about the SMART scan intention default change in v7, and provides the migration guide URL. Preserves the BarcodeCaptureListener implementation.",
"files": [
"fixtures/ViewControllerV6.swift"
],
"assertions": [
{"text": "DataCaptureContext.initialize(licenseKey: is present in the output"},
{"text": "DataCaptureContext(licenseKey: is NOT present in the output (deprecated v6 form removed)"},
{"text": "DataCaptureContext.shared is read after initialize"},
{"text": "BarcodeCapture.recommendedCameraSettings is used"},
{"text": "CameraSettings() with preferredResolution = .auto is NOT present in the output"},
{"text": "The user is informed about the new default SMART scan intention in v7"},
{"text": "The migration guide URL (https://docs.scandit.com/sdks/ios/migrate-6-to-7/) is provided"},
{"text": "BarcodeCapture(context:settings:) is preserved (not changed)"},
{"text": "The BarcodeCaptureListener implementation (barcodeCapture(_:didScanIn:frameData:)) is preserved"}
]
},
{
"id": 2,
"prompt": "I'm upgrading my Scandit iOS barcode scanning SDK from v7 to v8. What do I need to change in my code?",
"expected_output": "The skill explains that native iOS BarcodeCapture has no breaking API changes in v7→v8. The only change to watch for is VideoResolution.auto deprecation. It does NOT tell the user to replace BarcodeCapture(context:settings:) with a different constructor pattern (that change is for cross-platform SDKs only).",
"files": [],
"assertions": [
{"text": "The skill mentions that there are no breaking API changes for native iOS BarcodeCapture in v7→v8"},
{"text": "VideoResolution.auto (or .auto resolution) deprecation is mentioned"},
{"text": "The skill does NOT instruct the user to replace BarcodeCapture(context:settings:) with a different constructor + addMode pattern (that is a cross-platform SDK change)"},
{"text": "Migration guide URL (https://docs.scandit.com/sdks/ios/migrate-7-to-8/) is provided"}
]
},
{
"id": 3,
"prompt": "I want to upgrade my BarcodeCapture iOS integration to the latest Scandit SDK version. Here's my code and Package.resolved.",
"expected_output": "The skill reads Package.resolved to detect v6.28.1, determines the target is v8, applies both migrations (6→7 then 7→8) without asking the user for their version.",
"files": [
"fixtures/ViewControllerV6.swift",
"fixtures/Package_v6.resolved"
],
"assertions": [
{"text": "Summary mentions the detected version (6.28.1 or v6)"},
{"text": "Did not ask the user for their version — proceeded autonomously"},
{"text": "DataCaptureContext.initialize(licenseKey: is present (v6→v7 context update applied)"},
{"text": "DataCaptureContext(licenseKey: is NOT present"},
{"text": "BarcodeCapture.recommendedCameraSettings is used (camera setup updated)"},
{"text": "CameraSettings() with preferredResolution = .auto is NOT present"}
]
}
]
}
{
"skill_name": "barcode-capture-ios",
"evals": [
{
"id": 1,
"prompt": "I have an iOS app using AVFoundation's AVCaptureMetadataOutput for barcode scanning. I want to replace it with BarcodeCapture. Here is my view controller: AVFoundationViewController.swift",
"expected_output": "Removes all AVFoundation barcode-scanning APIs (AVCaptureSession, AVCaptureMetadataOutput, AVCaptureMetadataOutputObjectsDelegate, AVCaptureVideoPreviewLayer), adds BarcodeCapture integration with matching symbologies (.ean13UPCA, .code128, .qr), preserves ScannedBarcode struct and deduplication logic.",
"files": [
"fixtures/AVFoundationViewController.swift"
],
"assertions": [
{
"text": "import AVFoundation is NOT present in the output (may appear in a before/after summary table)"
},
{
"text": "AVCaptureSession is NOT instantiated in the new code"
},
{
"text": "AVCaptureMetadataOutput is NOT instantiated in the new code"
},
{
"text": "AVCaptureMetadataOutputObjectsDelegate conformance is NOT present on the view controller in the new code"
},
{
"text": "AVCaptureVideoPreviewLayer is NOT present in the new code"
},
{
"text": "import ScanditBarcodeCapture IS present"
},
{
"text": "BarcodeCaptureListener conformance is present (semantic)"
},
{
"text": "Symbology .ean13UPCA is enabled (not .ean13) (semantic)"
},
{
"text": "Symbology .code128 is enabled (semantic)"
},
{
"text": "Symbology .qr is enabled (not .qrCode, which does not exist as a Swift case) (semantic)"
},
{
"text": "DataCaptureView(context:frame:) is used and added as a subview"
},
{
"text": "BarcodeCaptureOverlay(barcodeCapture:view:) is called"
},
{
"text": "viewWillAppear turns the camera on (camera?.switch(toDesiredState: .on))"
},
{
"text": "viewWillDisappear turns the camera off (camera?.switch(toDesiredState: .off))"
},
{
"text": "ScannedBarcode struct is preserved in the output"
},
{
"text": "scannedBarcodes array is preserved in the output"
},
{
"text": "Deduplication logic (checking for existing value before appending) is preserved"
},
{
"text": "A summary of changes is shown with what was removed and what was added"
},
{
"text": "Setup checklist mentions adding the SDK via Swift Package Manager (datacapture-spm)"
},
{
"text": "Setup checklist mentions NSCameraUsageDescription in Info.plist"
}
]
}
]
}
BarcodeCapture iOS Integration Guide
BarcodeCapture is the low-level single-barcode scanning mode. Unlike SparkScan, there is no pre-built UI — you wire up a DataCaptureContext, a Camera frame source, the BarcodeCapture mode with a BarcodeCaptureListener, a DataCaptureView for the preview, and a BarcodeCaptureOverlay for the highlight.
Prerequisites
- Scandit Data Capture SDK for iOS — add via Swift Package Manager:
- URL:
https://github.com/Scandit/datacapture-spm - Add
ScanditBarcodeCaptureandScanditCaptureCorepackage products to your target - A valid Scandit license key:
- Sign in at https://ssl.scandit.com to generate one
- No account yet? Sign up at https://ssl.scandit.com/dashboard/sign-up?p=test
NSCameraUsageDescriptioninInfo.plist
Minimal Integration (Swift)
Ask the user which barcode symbologies they need to scan. When asking, mention that it's important to only enable the symbologies they actually need, as enabling fewer improves scanning performance and accuracy.
Once the user responds, ask them which file or view controller they'd like to integrate BarcodeCapture into. Then write the integration code directly into that file. Do not just show the code in chat; apply it to the file.
After providing the code, show this setup checklist:
Setup checklist: 1. Add ScanditBarcodeCapture and ScanditCaptureCore via Swift Package Manager: https://github.com/Scandit/datacapture-spm 2. Make sure you have NSCameraUsageDescription added to your Info.plist 3. Replace -- ENTER YOUR SCANDIT LICENSE KEY HERE -- with your key from https://ssl.scandit.com
The code example below is for UIKit. If the user is using SwiftUI, use the SwiftUI get-started guide and sample instead (see References in SKILL.md).
import UIKit
import ScanditBarcodeCapture
class ViewController: UIViewController {
private lazy var context: DataCaptureContext = {
// Enter your Scandit License key here.
// Your Scandit License key is available via your Scandit SDK web account.
DataCaptureContext.initialize(licenseKey: "-- ENTER YOUR SCANDIT LICENSE KEY HERE --")
return DataCaptureContext.shared
}()
private var camera: Camera?
private var barcodeCapture: BarcodeCapture!
private var captureView: DataCaptureView!
private var overlay: BarcodeCaptureOverlay!
override func viewDidLoad() {
super.viewDidLoad()
camera = Camera.default
camera?.apply(BarcodeCapture.recommendedCameraSettings)
context.setFrameSource(camera)
let settings = BarcodeCaptureSettings()
Set<Symbology>([.ean13UPCA, .code128]).forEach {
settings.set(symbology: $0, enabled: true)
}
barcodeCapture = BarcodeCapture(context: context, settings: settings)
barcodeCapture.addListener(self)
captureView = DataCaptureView(context: context, frame: view.bounds)
captureView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
view.addSubview(captureView)
overlay = BarcodeCaptureOverlay(barcodeCapture: barcodeCapture, view: captureView)
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
barcodeCapture.isEnabled = true
camera?.switch(toDesiredState: .on)
}
override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(animated)
barcodeCapture.isEnabled = false
camera?.switch(toDesiredState: .off)
}
}
extension ViewController: BarcodeCaptureListener {
func barcodeCapture(_ barcodeCapture: BarcodeCapture,
didScanIn session: BarcodeCaptureSession,
frameData: FrameData) {
guard let barcode = session.newlyRecognizedBarcode else { return }
DispatchQueue.main.async {
print("Scanned: \(barcode.data ?? "")")
// Handle the barcode here.
}
}
}Optional configuration
All of the following are applied to the BarcodeCaptureSettings instance (before BarcodeCapture(context:settings:), or re-applied later with barcodeCapture.apply(settings, completionHandler:)) or to the BarcodeCaptureOverlay. Use the exact Swift APIs below — many per-symbology options live on SymbologySettings, obtained via settings.settings(for:), not directly on BarcodeCaptureSettings.
Per-symbology settings
settings.settings(for:) returns a mutable SymbologySettings for one symbology. Mutate it, then apply the parent settings.
let symbologySettings = settings.settings(for: .code39)
// Symbology extensions (e.g. Code 39 full ASCII). Pass the extension name as a String.
symbologySettings.set(extension: "full_ascii", enabled: true)
// Optional checksums. `checksums` is a Checksum OptionSet — assign with an array literal.
symbologySettings.checksums = [.mod43]
// Active symbol counts (variable-length 1D codes). Type is Set<Int>.
settings.settings(for: .code128).activeSymbolCounts = Set(7...20)
// Color-inverted (light-on-dark) codes. This is a PER-SYMBOLOGY setting.
settings.settings(for: .qr).isColorInvertedEnabled = trueViewfinders
Assign a viewfinder to the overlay's viewfinder property (the overlay is created with BarcodeCaptureOverlay(barcodeCapture:view:)).
overlay.viewfinder = AimerViewfinder() // target dot to aim at
overlay.viewfinder = LaserlineViewfinder() // horizontal line for long 1D codesOverlay highlight brush
The brush draws recognized barcodes. Brush(fill:stroke:strokeWidth:) takes UIColor fill, UIColor stroke, and a CGFloat width.
overlay.brush = Brush(fill: UIColor.green.withAlphaComponent(0.2),
stroke: UIColor.green,
strokeWidth: 2)Rejecting barcodes
There is no session.rejectBarcodes(...) API. To reject codes whose data does not match, do the check inside didScanIn: set the overlay brush to Brush.transparent so the non-matching code is not highlighted, and return early before handling it.
`overlay.brush` is overlay-wide, not per-barcode. BarcodeCapture has no per-barcode brush delegate (unlike MatrixScan). Once you set the brush to transparent it stays transparent for every subsequent code — including matching ones — until you set it back. So in continuous scanning you MUST restore the brush on the accept path, e.g. overlay.brush = BarcodeCaptureOverlay.defaultBrush, or the preview goes permanently blank after the first rejected code.
Rejection only hides the highlight — it does not mute feedback. A rejected code is still recognized, so the default beep/vibration still fires. If "reject" should also be silent, mute feedback as well: barcodeCapture.feedback.success = Feedback(vibration: nil, sound: nil). Decide which you want: suppress only the highlight (below), or true silent filtering (highlight + feedback both off).
func barcodeCapture(_ barcodeCapture: BarcodeCapture,
didScanIn session: BarcodeCaptureSession,
frameData: FrameData) {
guard let barcode = session.newlyRecognizedBarcode,
let data = barcode.data else { return }
guard data.hasPrefix("978") else {
// Reject: hide the highlight and stop.
overlay.brush = Brush.transparent
return
}
// Accept: restore the default highlight so matching codes stay visible.
overlay.brush = BarcodeCaptureOverlay.defaultBrush
DispatchQueue.main.async {
// Handle the accepted barcode here.
}
}Composite codes
Enabling composite codes requires BOTH steps: set enabledCompositeTypes, and call enableSymbologies(forCompositeTypes:) to turn on the underlying symbologies. Setting enabledCompositeTypes alone is not sufficient. CompositeType is an OptionSet (.a, .b, .c).
settings.enabledCompositeTypes = [.a, .b]
settings.enableSymbologies(forCompositeTypes: [.a, .b])For other advanced configuration (custom feedback, duplicate filtering, location selection), see the Advanced Configurations and API reference linked from SKILL.md.
BarcodeCapture iOS Migration Guide
Step 1: Detect the installed SDK version
Before making any changes, find out which version of the Scandit SDK the project currently has installed.
Check in this order:
1. Swift Package Manager — open <ProjectRoot>/Package.resolved (or <ProjectRoot>/<App>.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved) and look for the entry with "identity": "datacapture-spm". The "version" field is the installed version. 2. CocoaPods — open Podfile.lock and look for ScanditBarcodeCapture or ScanditCaptureCore. The version number is on the same line.
Once you know the installed version, determine which migration path applies:
| Installed version | Target version | Action |
|---|---|---|
| 6.x | 7.x | Apply the 6 → 7 migration below |
| 7.x | 8.x | Apply the 7 → 8 migration below |
| 6.x | 8.x | Apply both migrations in order (6→7 first, then 7→8) |
If you cannot find Package.resolved or Podfile.lock, ask the user which version they are migrating from.
---
Step 2: Update the dependency version
Before touching source files, update the SDK version in the dependency manager:
- SPM: In Xcode → Package Dependencies, update
datacapture-spmto the target version. - CocoaPods: Update the version constraint in
Podfile, then runpod update.
Ask the user which dependency manager they use if it's not clear from the project.
---
Step 3: Apply source code changes
Find the files that use BarcodeCapture (search for BarcodeCapture, BarcodeCaptureSettings, BarcodeCaptureOverlay, BarcodeCaptureListener) and apply the relevant changes below directly to those files.
---
Migration: 6 → 7
The 6→7 step has three things to handle: a context-construction update, a camera setup update, and a scan intention behavioral change. Go through each section below and apply every change that matches the project — do not skip a section just because most of the API is unchanged.
Context construction — update to the v7+ shared singleton pattern
In v7+ the recommended way to construct the context is DataCaptureContext.initialize(licenseKey:) followed by reading DataCaptureContext.shared. The bare DataCaptureContext(licenseKey:) constructor is deprecated.
Before (v6 — replace this):
let context = DataCaptureContext(licenseKey: "-- KEY --")After (v7+):
DataCaptureContext.initialize(licenseKey: "-- KEY --")
let context = DataCaptureContext.sharedIf DataCaptureContext.shared is already in use, skip this section.
Camera setup — update to the v7+ recommended pattern
Look for any of these patterns in the project: an explicit CameraSettings() construction with no arguments, preferredResolution = .auto (or VideoResolution.auto), or camera?.apply(cameraSettings) where the settings were not obtained from BarcodeCapture.recommendedCameraSettings. If found, replace the block — BarcodeCapture.recommendedCameraSettings is the canonical API from v7 onwards, and updating during the v6→v7 migration avoids accumulating camera setup tech debt. Inform the user that VideoResolution.auto will be formally deprecated in v8.
Before (v6 pattern — remove this):
let cameraSettings = CameraSettings()
cameraSettings.preferredResolution = .auto
let camera = Camera.default
camera?.apply(cameraSettings)After (v7+ pattern — use this):
let camera = Camera.default
camera?.apply(BarcodeCapture.recommendedCameraSettings)If BarcodeCapture.recommendedCameraSettings is already in use, skip this section.
Scan intention default change
The default scan intention is now SMART from v7. Most projects need no action.
- If the project already explicitly sets
ScanIntention.manualor another value onBarcodeCaptureSettings, leave it as is. - If the project uses a single-image frame source, you must set
scanIntention = .manual— smart is incompatible with single-frame sources. - If the project did not set the property at all, scanning now uses the smart-scan algorithm by default. This is generally desirable; inform the user but do not change the code.
Composite codes default change
Default support for Composite Codes was removed when smart scan is enabled. If the project scans composite codes (CC-A, CC-B, CC-C), explicitly enable them. Fetch the BarcodeCapture API reference for the exact API — do not guess the method name.
If the project does not use composite codes, no action is needed.
---
Migration: 7 → 8
The 7→8 step for native iOS BarcodeCapture has no breaking API changes. The factory-method deprecations listed in the official migration guide apply to cross-platform SDKs (React Native, Flutter, Capacitor) — not to native Swift where BarcodeCapture(context:settings:) remains the correct API.
VideoResolution.auto deprecated
If the project creates a CameraSettings with VideoResolution.auto, replace it with the recommended camera settings:
v7 (deprecated):
let cameraSettings = CameraSettings()
cameraSettings.preferredResolution = .auto
camera?.apply(cameraSettings)v8:
camera?.apply(BarcodeCapture.recommendedCameraSettings)If the project already uses BarcodeCapture.recommendedCameraSettings, no action is needed.
No other breaking BarcodeCapture changes
The BarcodeCapture(context:settings:) constructor, BarcodeCaptureListener, BarcodeCaptureSession, and BarcodeCaptureOverlay(barcodeCapture:view:) are all unchanged in v8 for native iOS.
---
After applying changes
1. Build the project. Fix any remaining compile errors using the API reference (linked in SKILL.md). 2. Let the user know they can check the full list of SDK changes in the official migration guides:
- 6 → 7: https://docs.scandit.com/sdks/ios/migrate-6-to-7/
- 7 → 8: https://docs.scandit.com/sdks/ios/migrate-7-to-8/
3. Show the user a summary of only the changes actually made: which files were edited, which properties were renamed/removed, and anything that required a judgment call. Do not list APIs that were already correct or unchanged. 4. If compile errors persist after the changes above, fetch the BarcodeCapture API reference (https://docs.scandit.com/data-capture-sdk/ios/barcode-capture/api.html) to find the correct API before guessing.
Third-Party Barcode Scanner → BarcodeCapture Migration (iOS)
Before Anything Else
Read the existing code. Do not ask the user to describe what their scanner does. Identify:
- Which framework is in use (read the imports)
- Which symbologies are enabled
- What result handling logic exists (deduplication, filtering, accumulation)
- What data models are defined
- How the scanner is presented (modal, embedded, full-screen, navigation push)
Remove
- The old framework's imports
- The scanner / detector class instance and all its setup code
- The old delegate or callback conformance
- Any UI presentation code specific to the old scanner (e.g. modal presentation,
AVCaptureVideoPreviewLayer, intent launch)
Integrate BarcodeCapture
Follow references/integration.md. When configuring BarcodeCaptureSettings, map the symbologies from the old scanner. Scandit symbology names differ from other libraries — verify each one against the BarcodeCapture API reference rather than guessing from the old framework's name.
Preserve
- Custom data models — keep as-is
- Result accumulation and deduplication logic — move verbatim into the
barcodeCapture(_:didScanIn:frameData:)callback - Any downstream business logic triggered on scan result
When done, show only what changed. Do not list APIs that were unchanged.
Related skills
How it compares
SDK integration skill for on-device capture—not a web ZXing snippet or a generic ‘add camera permission’ checklist.
FAQ
Who is barcode-capture-ios for?
Developers and small teams shipping UIKit or SwiftUI-hosted iOS apps who want agent help moving from Apple’s metadata output to Scandit Barcode Capture.
When should I use barcode-capture-ios?
In the Build frontend subphase while implementing or migrating in-app barcode scanning, camera preview, and scan result handling on iPhone/iPad.
Is barcode-capture-ios safe to install?
Check the Security Audits panel on this page; the skill implies camera access and third-party SDK binaries—review Scandit licensing and app privacy strings yourself.