Developing QR Code Scanning in a Mobile App
We often encounter the task of integrating a QR code scanner into an existing app. At first glance, there are many libraries, but each platform imposes its own rules: on iOS, strict App Store limitations; on Android, fragmentation of devices and OS versions. The result: the app either freezes on scanning or parses data incorrectly. Over 5+ years, we have implemented QR scanners in 20+ projects — from retail to healthcare, using Swift, Kotlin, and Flutter. In this article, we break down how to build a fast, reliable, and modular QR scanner considering all nuances: from framework selection (Vision vs ML Kit) to correct routing by data type.
According to Apple documentation, using DataScannerViewController requires setting up an entitlement and adding NSCameraUsageDescription. On Android, the CAMERA permission is required in AndroidManifest.xml. Skipping these steps is one of the most common causes of crashes on first launch.
How to Quickly Implement QR Code Scanning?
Scanning is only the first step. iOS 16+ uses DataScannerViewController — a one-liner setup:
let scanner = DataScannerViewController( recognizedDataTypes: [.barcode(symbologies: [.qr])], qualityLevel: .balanced, isHighlightingEnabled: true ) isHighlightingEnabled: true adds visual highlighting — users appreciate that. For iOS 14–15, you need the Vision framework: VNDetectBarcodesRequest for photos, AVCaptureMetadataOutput for live video. On Android, we use ML Kit: BarcodeScanning.getClient() with BarcodeScannerOptions. Specify FORMAT_QR_CODE to optimize speed — if you need all formats, remove the filter, but speed drops slightly.
ML Kit on Android processes QR 3–5 times faster than ZXing, and 2 times faster than Vision on iOS on mid-range devices (data from our tests on 10 models).
Comparison of iOS and Android Approaches
| Parameter | iOS | Android |
|---|---|---|
| Primary framework | DataScannerViewController (iOS 16+) / Vision |
ML Kit BarcodeScanning |
| Additional setup | Provisioning profile, NSCameraUsageDescription |
AndroidManifest.xml camera request |
| Gallery handling | PHPicker + Vision |
GetContent + ML Kit |
| Type detection | Manual by string | Built-in via Barcode.valueType |
| Performance | Instant (system-level) | 50–200 ms on mid-range devices |
Why Content Parsing Is More Important Than Scanning Itself?
A QR code is a string. How you handle it determines UX.
- Starts with
http://orhttps://→ open inSFSafariViewController/ CustomTabs. -
WIFI:S:NetworkName;T:WPA;P:password;;→ connect to Wi-Fi (iOS:NEHotspotConfiguration, Android:WifiNetworkSuggestion). -
BEGIN:VCARD→ parse withCNContactVCardSerialization(iOS) orVCardReader(Android). - Internal app format → custom processing.
Regular expressions for type detection work but are fragile. ML Kit on Android automatically detects the type via Barcode.valueType (URL, WIFI, CONTACT_INFO, etc.). On iOS, VNBarcodeObservation.payloadStringValue returns the raw string — you must determine the type yourself. For Wi-Fi on iOS, you need com.apple.developer.networking.HotspotConfiguration entitlement — a hidden complexity.
QR Data Types and Their Handling
| Type | Example Content | iOS | Android |
|---|---|---|---|
| URL | https://example.com |
SFSafariViewController | CustomTabs |
| Wi-Fi | WIFI:S:... |
NEHotspotConfiguration | WifiNetworkSuggestion |
| vCard | BEGIN:VCARD... |
CNContactVCardSerialization | VCardReader |
| Custom | Any format | Your logic | Your logic |
Gallery Handling: A Must-Have Minimum
Users expect to be able to pick a QR code from a photo, not just scan with the camera. Implementation:
iOS: PHPickerViewController → get UIImage → VNDetectBarcodesRequest on CIImage. Runs in background via perform.
Android: ActivityResultContracts.GetContent("image/*") → get Uri → InputImage.fromFilePath(context, uri) → BarcodeScanning.getClient().process(inputImage).
Common mistake: processing image on the main thread. For gallery photos, this blocks the UI for 200–500 ms. Always do it in a background thread. Our experience shows users notice delays as low as 150 ms.
Turnkey Development Process
- Analysis — we study your app, target audience, and QR usage scenarios.
- Design — choose the stack (ML Kit vs Vision, routing implementation), prepare a prototype with one code type.
- Implementation — integrate scanner, parsing, gallery handling, deep linking.
- Testing — on real devices with different OS versions, verify all QR formats.
- Deployment — publish to App Store / Google Play, configure provisioning profiles.
What's Included
- Source code of the scanning module in Swift / Kotlin / Dart with documentation.
- Integration with your app's existing navigator.
- Handling of all common QR types (URL, Wi-Fi, vCard, custom).
- Gallery support.
- Testing on 5+ physical devices.
- Assistance with publishing (App Store Connect, Google Play Console).
Estimated Timelines
From 3 to 10 business days, depending on routing complexity and need for deep linking. Contact us so we can evaluate your project and provide an accurate cost estimate.
Typical Mistakes in QR Scanner Development
- Not handling camera permission at runtime — the app crashes on iOS if the user denies the request.
- Synchronous image processing on the UI thread — lag when scanning from gallery.
- Ignoring
ATT(App Tracking Transparency) on iOS — ban from App Store. - No fallback for older OS versions — outdated libraries not updated.
Our Experience
Over 5+ years, we have implemented QR scanners in 20+ apps, from retail to healthcare. We have worked with Apple certificates, ProGuard/R8 shrinking, Firebase App Distribution. We have experience navigating App Store review with custom entitlements. If you have a specific case — reach out, let's discuss.
Time savings for users on manual data entry reach up to 80% when using a QR scanner. Order a turnkey QR scanner module development. Get a consultation on integration and timeline estimation — contact us.







