CodeScanner is a modern SwiftUI library for scanning barcodes and QR codes. It is optimised for retail barcode scanning out of the box, with sensible defaults that cover EAN-13, EAN-8, UPC-E, Code 128, and Code 39 — the symbologies found on virtually every retail product worldwide.
Originally forked from twostraws/CodeScanner, then modernised with Swift 6 concurrency, Sendable types, @MainActor isolation, and retail-focused defaults.
- Retail-optimised defaults (EAN-13, UPC, Code 128, Code 39)
- Swift 6 strict concurrency throughout
@MainActor-isolated view controller with off-main-thread capture session setup- Auto-zoom for small barcode readability
- Tap-to-focus for precise targeting
- Continuous scanning mode for high-throughput retail workflows
- Code-drawn viewfinder overlay (no image assets required)
- Preset barcode type collections (
BarcodeType.retail,.allBarcodes,.all2D,.all) - Configurable camera selection (wide, ultra-wide, auto, custom)
- Privacy manifest included
- iOS 16.0+
- Swift 6.0+
- Xcode 16.0+
Add CodeScanner to your project via SPM:
dependencies: [
.package(url: "https://github.com/cymr0/CodeScanner.git", branch: "main")
]The simplest usage — scan retail barcodes with one line:
CodeScannerView { result in
switch result {
case .success(let scan):
print("Scanned: \(scan.payload) (\(scan.symbology))")
case .failure(let error):
print("Error: \(error.localizedDescription)")
}
}This uses all retail defaults: continuous scanning, EAN-13/UPC/Code 128/Code 39 detection, 1-second scan interval, viewfinder overlay, and wide-angle camera with auto-zoom.
// Retail products (EAN-13, EAN-8, UPC-E, Code 128, Code 39)
CodeScannerView(codeTypes: BarcodeType.retail) { ... }
// All 1D barcodes (adds Code 93, ITF-14, Interleaved 2of5, Codabar, etc.)
CodeScannerView(codeTypes: BarcodeType.allBarcodes) { ... }
// 2D codes only (QR, PDF417, DataMatrix, Aztec)
CodeScannerView(codeTypes: BarcodeType.all2D) { ... }
// Everything
CodeScannerView(codeTypes: BarcodeType.all) { ... }// Scan once and stop
CodeScannerView(scanMode: .once) { ... }
// Each unique code triggers once
CodeScannerView(scanMode: .oncePerCode) { ... }
// Continuous scanning (default for retail)
CodeScannerView(scanMode: .continuous, scanInterval: 1.0) { ... }
// Continuous but skip known codes
CodeScannerView(scanMode: .continuousExcept(ignoredList: ["123456"]), scanInterval: 1.0) { ... }
// Manual trigger only
CodeScannerView(scanMode: .manual) { ... }// Use the retail preset (default)
CodeScannerView(cameraConfiguration: .retail) { ... }
// Custom configuration
var config = CameraConfiguration.retail
config.isTorchEnabled = true
config.preferredCamera = .ultraWide
config.minimumCodeSize = 15 // mm
CodeScannerView(cameraConfiguration: config) { ... }struct ScannerScreen: View {
@State private var isPresentingScanner = false
@State private var lastScannedCode: String?
var body: some View {
VStack(spacing: 20) {
if let code = lastScannedCode {
Text("Last scan: \(code)")
.font(.headline)
}
Button("Scan Barcode") {
isPresentingScanner = true
}
}
.sheet(isPresented: $isPresentingScanner) {
CodeScannerView(
codeTypes: BarcodeType.retail,
scanMode: .continuous,
scanInterval: 1.0,
showViewfinder: true,
isTorchOn: false
) { result in
if case let .success(scan) = result {
lastScannedCode = scan.payload
isPresentingScanner = false
}
}
}
}
}CodeScannerView { result in
if case let .success(scan) = result {
if scan.isBarcode {
print("1D barcode: \(scan.payload)")
} else {
print("2D code: \(scan.payload)")
}
}
}Important: iOS requires you to add the "Privacy - Camera Usage Description" key to your Info.plist file.
| Type | Description |
|---|---|
CodeScannerView |
Main SwiftUI view — drop-in barcode scanner |
ScanResult |
Decoded payload, symbology, corners, optional image |
ScanError |
Error cases: camera unavailable, permission denied, etc. |
ScanMode |
Scanning behaviour: once, oncePerCode, continuous, manual |
BarcodeType |
Preset symbology collections for common use cases |
CameraConfiguration |
Camera hardware settings (lens, focus, zoom, torch) |
CameraSelection |
Which physical camera to use |
ScannerViewController |
Underlying UIKit controller (for advanced use) |
Originally created by Paul Hudson. Modernised for Swift 6 and retail barcode scanning.
MIT License. See LICENSE for details.