QR Codes
This is a free Skip Swift/Kotlin library project containing the following modules:
SkipQRCode
Installation
Section titled “Installation”Add SkipQRCode as a dependency in your Package.swift:
dependencies: [ .package(url: "https://source.skip.tools/skip-qrcode.git", "0.0.1"..<"2.0.0")],targets: [ .target( name: "YourApp", dependencies: [ .product(name: "SkipQRCode", package: "skip-qrcode") ] )]Quick Start
Section titled “Quick Start”Basic Usage
Section titled “Basic Usage”import SwiftUIimport SkipQRCode
struct ContentView: View { @State private var showScanner = false @State private var scannedCode: String?
var body: some View { VStack(spacing: 20) { Button("Scan QR Code") { showScanner = true } .buttonStyle(.borderedProminent)
if let code = scannedCode { Text("Scanned: \(code)") .font(.headline) } } .sheet(isPresented: $showScanner) { #if os(iOS) BarcodeScannerView { code in scannedCode = code showScanner = false } .ignoresSafeArea() #elseif os(Android) // Android scanner launches as a native activity Color.clear .onAppear { showScanner = false AndroidBarcodeScanner.scan { code in if let code = code { scannedCode = code } } } #endif } }}Advanced Example with Error Handling
Section titled “Advanced Example with Error Handling”import SwiftUIimport SkipQRCode
struct AdvancedScannerView: View { @State private var showScanner = false @State private var scannedCode: String? @State private var scanError: String? @State private var isScanning = false
var body: some View { VStack(spacing: 20) { Text("QR Code Scanner") .font(.title)
if let code = scannedCode { VStack(alignment: .leading, spacing: 8) { Text("Scanned Code:") .font(.caption) .foregroundColor(.secondary) Text(code) .font(.body) .padding() .background(Color.gray.opacity(0.1)) .cornerRadius(8) } .padding() }
if let error = scanError { Text("Error: \(error)") .foregroundColor(.red) .padding() }
Button(action: startScanning) { HStack { Image(systemName: "qrcode.viewfinder") Text(isScanning ? "Scanning..." : "Scan QR Code") } } .buttonStyle(.borderedProminent) .disabled(isScanning) } .padding() .sheet(isPresented: $showScanner) { #if os(iOS) BarcodeScannerView { code in handleScanResult(code) } .ignoresSafeArea() #elseif os(Android) Color.clear .onAppear { showScanner = false performAndroidScan() } #endif } }
private func startScanning() { scannedCode = nil scanError = nil isScanning = true showScanner = true }
private func handleScanResult(_ code: String) { scannedCode = code showScanner = false isScanning = false }
#if os(Android) private func performAndroidScan() { AndroidBarcodeScanner.scan { code in isScanning = false if let code = code { scannedCode = code } else { scanError = "Scan cancelled or failed" } } } #endif}Platform-Specific Behavior
Section titled “Platform-Specific Behavior”Implementation: Uses VisionKit’s DataScannerViewController
- Requires iOS 17.0+
- Full-screen camera interface
- Automatic barcode detection
- Built-in guidance overlay
- Pinch-to-zoom support
- High frame rate tracking
Permissions: Add camera usage description to your Info.plist:
<key>NSCameraUsageDescription</key><string>We need camera access to scan QR codes and barcodes</string>Android
Section titled “Android”Implementation: Uses Google ML Kit with CameraX
- Requires Android API 24+
- Native activity-based scanner
- ML Kit barcode detection
- Torch/flashlight control
- Portrait orientation
Permissions: Automatically merged from the package’s AndroidManifest.xml:
android.permission.CAMERA- Camera hardware features (optional)
Activities: Scanner activities are automatically registered:
skip.qrcode.MLKitScanActivity- Main scanner with cameraskip.qrcode.ScanHostActivity- Activity launcher/bridge
API Reference
Section titled “API Reference”AndroidBarcodeScanner
Section titled “AndroidBarcodeScanner”A utility struct for launching the Android scanner.
public struct AndroidBarcodeScanner { public static func scan(completion: @escaping @Sendable (String?) -> Void)}Parameters:
completion: Async callback invoked when scanning completes. Receives:- The scanned barcode string if successful
nilif the scan was cancelled or failed
Usage:
AndroidBarcodeScanner.scan { result in if let barcode = result { print("Scanned: \(barcode)") } else { print("Scan cancelled or failed") }}Supported Barcode Types
Section titled “Supported Barcode Types”SkipQRCode supports all common 1D and 2D barcode formats:
- 2D Codes: QR Code, Data Matrix, Aztec, PDF417
- 1D Codes: UPC-A, UPC-E, EAN-8, EAN-13
- Other: Code 39, Code 93, Code 128, ITF, Codabar
- Specialized: Driver’s License, Calendar Events, Contact Info
Architecture
Section titled “Architecture”SkipQRCode is a transpiled Skip package with bridging support, which means:
- ✅ iOS: Direct Swift → VisionKit (native iOS framework)
- ✅ Android: Swift → Transpiled Kotlin → ML Kit + CameraX
- ✅ Bridging: Automatically bridges to native SkipFuse apps
- ✅ Platform Optimized: Uses best-in-class libraries for each platform
- ✅ Zero Setup: Import once, works on both platforms
Component Overview
Section titled “Component Overview”iOS Side: SwiftUI → VisionKit DataScannerViewController
Android Side (Transpiled): Swift → AndroidBarcodeScanner.swift ↓ (transpiles to) Kotlin → ScanHostActivity → MLKitScanActivity (activity trampoline) (CameraX + ML Kit scanner)How It Works
Section titled “How It Works”- On iOS: Uses Apple’s VisionKit for native barcode scanning
- On Android:
- Swift code transpiles to Kotlin
- Launches native Android Activities with ML Kit
- Camera preview rendered programmatically (no XML resources)
- Results passed back via polling mechanism
Requirements
Section titled “Requirements”- iOS: 17.0 or later
- Android: API 24 (Android 7.0) or later
- Skip: 1.6.27 or later
- Xcode: 15.0 or later
Building
Section titled “Building”This project uses the Skip plugin with native compilation mode.
Install Skip using Homebrew ↗:
brew install skiptools/skip/skipBuild the package:
swift buildTesting
Section titled “Testing”Run tests using:
swift testFor parity testing across platforms:
skip testTroubleshooting
Section titled “Troubleshooting”Scanner doesn’t appear:
- Ensure you’re running on iOS 17.0 or later
- Check that
NSCameraUsageDescriptionis in your Info.plist - Verify camera permissions are granted
Scanner shows but doesn’t scan:
- Check that
DataScannerViewController.isSupportedreturns true - Ensure good lighting conditions
- Try different barcode types
Android
Section titled “Android”Scanner crashes on launch:
- Verify all dependencies are properly resolved
- Check that camera permissions are declared in manifest
- Ensure device has a working camera
Scanned results not returning:
- Check LogCat for error messages (tag:
MLKitScanActivity,ScanHostActivity) - Ensure ML Kit dependencies are included
- Verify network connectivity (ML Kit may download models)
Contributing
Section titled “Contributing”Contributions are welcome! Please feel free to submit a Pull Request.
Development Setup
Section titled “Development Setup”- Clone the repository
- Install Skip:
brew install skiptools/skip/skip - Open in Xcode or your preferred IDE
- Make your changes
- Run tests:
swift testorskip test - Submit a PR
Credits
Section titled “Credits”Built with Skip - Swift for iOS and Android
Technologies Used:
- iOS: VisionKit, SwiftUI
- Android: ML Kit, CameraX, Kotlin, Jetpack Compose