@cleanuidev/react-native-scanner 1.0.0-beta.3 β†’ 1.0.0-beta.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  # πŸ“± React Native Scanner
4
4
 
5
- **A powerful, native barcode and QR code scanner for React Native with configurable target area scanning**
5
+ **A powerful, native barcode and QR code scanner for React Native with configurable target area scanning. Limit scan area and restrict scanning to a specific region.**
6
6
 
7
7
  ![React Native Barcode Scanner Demo - QR Code and Barcode Scanning with Target Area](./preview.gif)
8
8
 
@@ -13,7 +13,7 @@
13
13
 
14
14
  **Built with ❀️ by [CleanUI.dev](https://cleanui.dev)**
15
15
 
16
- [Features](#-features) β€’ [Installation](#-installation) β€’ [Documentation](#-documentation)
16
+ [Features](#-features) β€’ [Why Choose](#-why-choose-this-library) β€’ [Comparison](#-comparison-with-other-libraries) β€’ [Quick Start](#-quick-start) β€’ [Installation](#-installation) β€’ [FAQ](#-frequently-asked-questions)
17
17
 
18
18
  </div>
19
19
 
@@ -36,6 +36,56 @@
36
36
 
37
37
  ---
38
38
 
39
+ ## 🎯 Why Choose @cleanuidev/react-native-scanner?
40
+
41
+ - **🎯 Target Area Scanning**: Unlike other libraries, built-in support for limiting scan area, restricting scanning region, and scanning within configurable target areas for precise detection
42
+ - **πŸš€ High Performance**: Uses native CameraX & ML Kit (Android) and AVFoundation & Vision (iOS) for optimal performance
43
+ - **πŸ“± New Architecture Ready**: Full support for React Native's new architecture (Fabric) on both platforms
44
+ - **πŸ”§ Easy Integration**: Simple API with sensible defaults - get started in minutes
45
+ - **πŸ“Š Multiple Scan Strategies**: Process one, all, largest, or sorted barcodes with built-in strategies
46
+ - **🎨 Highly Customizable**: Configurable target areas, barcode frames, and scanning behavior
47
+ - **πŸ“¦ Lightweight**: Minimal dependencies, optimized bundle size
48
+ - **βœ… Active Maintenance**: Regularly updated with bug fixes and new features
49
+
50
+ ---
51
+
52
+ ## πŸ†š Comparison with Other Libraries
53
+
54
+ | Feature | @cleanuidev/react-native-scanner | react-native-vision-camera | expo-camera |
55
+ |---------|--------------------------------|---------------------------|-------------|
56
+ | **Target Area Scanning / Limit Scan Area** | βœ… Built-in (limit scan area, restrict scan region) | ❌ Manual implementation | ❌ Manual implementation |
57
+ | **New Architecture Support** | βœ… Full Fabric support | βœ… Yes | βœ… Yes |
58
+ | **Native Performance** | βœ… CameraX + ML Kit / AVFoundation + Vision | βœ… Yes | βœ… Yes |
59
+ | **Expo Support** | βœ… Dev builds | βœ… Dev builds | βœ… Expo Go |
60
+ | **Barcode Frame Visualization** | βœ… Built-in | ⚠️ Custom | ❌ No |
61
+ | **Multiple Scan Strategies** | βœ… 4 strategies (ONE, ALL, BIGGEST, SORT_BY_BIGGEST) | ⚠️ Custom | ❌ No |
62
+ | **Setup Complexity** | ⭐⭐ Simple | ⭐⭐⭐ Moderate | ⭐ Easy |
63
+ | **Bundle Size** | Small | Medium | Small |
64
+ | **Active Maintenance** | βœ… Active | βœ… Active | βœ… Active |
65
+ | **License** | MIT | MIT | MIT |
66
+ | **Best For** | Barcode/QR scanning with target areas | General camera + scanning | Expo projects |
67
+
68
+ > **πŸ’‘ When to choose this library**: If you need barcode/QR code scanning with the ability to limit scan area, restrict scanning region, or scan within target areas, this library provides a simpler API and built-in features compared to general-purpose camera libraries.
69
+
70
+ ---
71
+
72
+ ## ⚑ Quick Start
73
+
74
+ ```bash
75
+ npm install @cleanuidev/react-native-scanner
76
+ ```
77
+
78
+ ```tsx
79
+ import ScannerView, { BarcodeFormat } from '@cleanuidev/react-native-scanner';
80
+
81
+ <ScannerView
82
+ barcodeTypes={[BarcodeFormat.QR_CODE]}
83
+ onBarcodeScanned={(e) => console.log(e.nativeEvent)}
84
+ />
85
+ ```
86
+
87
+ ---
88
+
39
89
  ## πŸ“¦ Installation
40
90
 
41
91
  ### Install Beta Version
@@ -53,9 +103,9 @@ yarn add @cleanuidev/react-native-scanner@beta
53
103
  To install a specific beta version:
54
104
 
55
105
  ```bash
56
- npm install @cleanuidev/react-native-scanner@1.0.0-beta.1
106
+ npm install @cleanuidev/react-native-scanner@1.0.0-beta.4
57
107
  # or
58
- yarn add @cleanuidev/react-native-scanner@1.0.0-beta.1
108
+ yarn add @cleanuidev/react-native-scanner@1.0.0-beta.4
59
109
  ```
60
110
 
61
111
  > **Note**: Once the library reaches stable release (1.0.0), you can install it without the `@beta` tag:
@@ -67,7 +117,71 @@ yarn add @cleanuidev/react-native-scanner@1.0.0-beta.1
67
117
 
68
118
  ## Platform Setup
69
119
 
70
- ### Android Setup
120
+ ### Expo Setup
121
+
122
+ > **⚠️ Important**: This library uses native code and requires an **Expo development build**. It cannot run in Expo Go.
123
+
124
+ #### Prerequisites
125
+
126
+ - Expo SDK 53+ (recommended)
127
+ - EAS CLI installed: `npm install -g eas-cli`
128
+ - Expo development build configured
129
+
130
+ #### Installation
131
+
132
+ 1. **Install the package:**
133
+
134
+ ```bash
135
+ npx expo install @cleanuidev/react-native-scanner
136
+ ```
137
+
138
+ 2. **Configure app.json or app.config.js:**
139
+
140
+ Add camera permissions directly to your Expo configuration:
141
+
142
+ ```json
143
+ {
144
+ "expo": {
145
+ "ios": {
146
+ "infoPlist": {
147
+ "NSCameraUsageDescription": "This app needs camera access to scan barcodes and QR codes"
148
+ }
149
+ },
150
+ "android": {
151
+ "permissions": [
152
+ "android.permission.CAMERA",
153
+ "android.permission.WAKE_LOCK"
154
+ ]
155
+ }
156
+ }
157
+ }
158
+ ```
159
+
160
+ 3. **Create a development build:**
161
+
162
+ For iOS:
163
+ ```bash
164
+ eas build --profile development --platform ios
165
+ ```
166
+
167
+ For Android:
168
+ ```bash
169
+ eas build --profile development --platform android
170
+ ```
171
+
172
+ Or build locally:
173
+ ```bash
174
+ npx expo run:ios
175
+ # or
176
+ npx expo run:android
177
+ ```
178
+
179
+ 5. **Install the development build on your device:**
180
+
181
+ - For EAS builds: Download and install the build from the EAS dashboard
182
+ - For local builds: The build will be installed automatically
183
+
184
+ ### Android Setup (Bare React Native)
71
185
 
72
186
  Add the following permissions to your `android/app/src/main/AndroidManifest.xml`:
73
187
 
@@ -79,7 +193,7 @@ Add the following permissions to your `android/app/src/main/AndroidManifest.xml`
79
193
  <uses-feature android:name="android.hardware.camera.flash" android:required="false" />
80
194
  ```
81
195
 
82
- ### iOS Setup
196
+ ### iOS Setup (Bare React Native)
83
197
 
84
198
  For iOS, add camera usage description to your `ios/YourApp/Info.plist`:
85
199
 
@@ -129,7 +243,7 @@ const styles = StyleSheet.create({
129
243
  });
130
244
  ```
131
245
 
132
- ### Scanner with Target Area
246
+ ### Scanner with Target Area - Limit Scan Area Example
133
247
 
134
248
  ```tsx
135
249
  import React, { useState } from 'react';
@@ -139,9 +253,9 @@ import ScannerView, { BarcodeFormat } from '@cleanuidev/react-native-scanner';
139
253
  export default function FocusAreaScanner() {
140
254
  const [torchEnabled, setTorchEnabled] = useState(false);
141
255
 
142
- // Target area configuration
256
+ // Target area configuration - limit scan area to specific region
143
257
  const focusAreaConfig = {
144
- enabled: true, // Only scan barcodes within the target area
258
+ enabled: true, // Limit scan area - only scan barcodes within the target area
145
259
  showOverlay: true, // Show overlay outside the target area
146
260
  size: 300, // Size of the target area (square)
147
261
  color: '#00FF00', // Color of the target area border
@@ -198,7 +312,7 @@ export default function FocusAreaScanner() {
198
312
  | Prop | Type | Default | Description |
199
313
  |------|------|---------|-------------|
200
314
  | `barcodeTypes` | `BarcodeFormat[]` | `[BarcodeFormat.QR_CODE]` | Array of barcode formats to scan |
201
- | `focusArea` | `FocusAreaConfig` | - | Target area configuration for precise scanning |
315
+ | `focusArea` | `FocusAreaConfig` | - | Limit scan area and restrict scanning region - target area configuration for precise scanning |
202
316
  | `barcodeFrames` | `BarcodeFramesConfig` | - | Barcode frame visualization configuration |
203
317
  | `torch` | `boolean` | `false` | Enable/disable torch/flashlight |
204
318
  | `zoom` | `number` | `1.0` | Camera zoom level |
@@ -213,8 +327,8 @@ export default function FocusAreaScanner() {
213
327
 
214
328
  ```tsx
215
329
  type FocusAreaConfig = {
216
- enabled?: boolean; // Whether to restrict scanning to target area only
217
- size?: FrameSize; // Size of the target area
330
+ enabled?: boolean; // Limit scan area - whether to restrict scanning to target area only
331
+ size?: FrameSize; // Size of the target area (scan region limit)
218
332
  color?: string; // Color of target area border
219
333
  showOverlay?: boolean; // Whether to draw overlay outside the target area
220
334
  };
@@ -311,9 +425,9 @@ BarcodeFormat.ITF // ITF (Interleaved 2 of 5)
311
425
  }
312
426
  ```
313
427
 
314
- ## Target Area Configuration
428
+ ## Target Area Configuration - Limit Scan Area
315
429
 
316
- The target area feature provides precise control over where barcodes are scanned:
430
+ The target area feature provides precise control over where barcodes are scanned. You can limit scan area, restrict scanning region, and confine barcode detection to a specific area on the screen:
317
431
 
318
432
  ### Basic Target Area
319
433
 
@@ -328,20 +442,24 @@ The target area feature provides precise control over where barcodes are scanned
328
442
  />
329
443
  ```
330
444
 
331
- ### Target Area with Restricted Scanning
445
+ ### Limit Scan Area - Restricted Scanning
446
+
447
+ Limit scan area and restrict scanning to a specific region:
332
448
 
333
449
  ```tsx
334
450
  <ScannerView
335
451
  focusArea={{
336
- enabled: true, // Only scan within target area
452
+ enabled: true, // Limit scan area - only scan barcodes within the target area
337
453
  showOverlay: true, // Show overlay outside the target area
338
454
  size: 300, // 300x300 pixel square
339
455
  color: '#00FF00', // Green border
340
456
  }}
341
- // Only scans within the target area
457
+ // Only scans within the limited scan area
342
458
  />
343
459
  ```
344
460
 
461
+ This configuration restricts scanning region and limits scan area to the defined target area only.
462
+
345
463
  ### Rectangular Target Area
346
464
 
347
465
  ```tsx
@@ -582,6 +700,52 @@ const requestCameraPermission = async () => {
582
700
 
583
701
  See the `example/` directory for complete working examples, including the "New Props Example" that demonstrates the updated prop structure.
584
702
 
703
+ ## ❓ Frequently Asked Questions
704
+
705
+ ### Is this library compatible with Expo?
706
+ Yes! This library works with Expo development builds (SDK 53+). It does not work with Expo Go due to native code requirements. See the [Expo Setup](#expo-setup) section for detailed instructions.
707
+
708
+ ### Does it support React Native's new architecture?
709
+ Yes! Full support for Fabric (new architecture) on both Android and iOS. The library is built with the new architecture in mind.
710
+
711
+ ### How does it compare to react-native-vision-camera?
712
+ This library focuses specifically on barcode/QR code scanning with built-in target area support and multiple scan strategies. `react-native-vision-camera` is a general-purpose camera library that requires more setup for barcode scanning. If you only need barcode scanning, this library provides a simpler API and built-in features.
713
+
714
+ ### Can I limit the scan area or restrict scanning to a specific region?
715
+ Yes! You can limit the scan area and restrict scanning to a specific region. The target area is optional. By default, you can scan the entire camera view. Set `focusArea.enabled: true` to limit scan area and restrict scanning to a specific region only.
716
+
717
+ ### Is it possible to limit scan area?
718
+ Yes! This library provides built-in support for limiting scan area. You can restrict scanning to a specific region using the `focusArea` prop with `enabled: true`. This allows you to limit scan area to a defined region on the screen, perfect for precise barcode detection.
719
+
720
+ ### Can I scan barcodes outside the target area?
721
+ Yes! The target area is optional. By default, you can scan the entire camera view. Set `focusArea.enabled: true` to restrict scanning to a specific area.
722
+
723
+ ### What barcode formats are supported?
724
+ QR Code, Code128, Code39, EAN-13, EAN-8, UPC-A, UPC-E, Data Matrix, PDF417, Aztec, and ITF (Interleaved 2 of 5). See the [Barcode Formats](#barcode-formats) section for the complete list.
725
+
726
+ ### Is it production-ready?
727
+ The library is currently in beta (1.0.0-beta.4) but is stable and actively maintained. Production use is recommended with proper testing. We're working towards a stable 1.0.0 release.
728
+
729
+ ### Does it work with React Native 0.83+?
730
+ Yes! The library supports React Native 0.83 and newer versions, including full support for the new architecture.
731
+
732
+ ### Can I customize the target area appearance?
733
+ Yes! You can customize the target area size, position, color, and overlay. See the [Target Area Configuration](#target-area-configuration) section for details.
734
+
735
+ ### How do I handle multiple barcodes?
736
+ The library supports multiple scan strategies: process all barcodes, only the first one, only the largest, or all sorted by size. See the [Barcode Scan Strategy](#barcode-scan-strategy) section.
737
+
738
+ ## πŸ’Ό Use Cases
739
+
740
+ - **Retail & E-commerce**: Product barcode scanning for inventory and checkout (limit scan area for precise product detection)
741
+ - **Inventory Management**: Stock tracking and warehouse management systems (restrict scan region for accurate scanning)
742
+ - **Event Management**: QR code ticket scanning and attendee check-in (limit scan area for ticket validation)
743
+ - **Authentication**: QR code-based login and two-factor authentication (restrict scanning region for security)
744
+ - **Payment Systems**: QR code payment processing and transaction scanning (limit scan area for payment codes)
745
+ - **Document Management**: Document barcode scanning and tracking (restrict scan region for document processing)
746
+ - **Asset Tracking**: QR/barcode-based asset management systems (limit scan area for asset identification)
747
+ - **Healthcare**: Medical device and medication barcode scanning (restrict scanning region for medical accuracy)
748
+
585
749
  ## Contributing
586
750
 
587
751
  1. Fork the repository
package/Scanner.podspec CHANGED
@@ -11,7 +11,7 @@ Pod::Spec.new do |s|
11
11
  s.authors = package["author"]
12
12
 
13
13
  s.platforms = { :ios => min_ios_version_supported }
14
- s.source = { :git => "https://github.com/rahulgwebdev/react-native-scanner.git", :tag => "#{s.version}" }
14
+ s.source = { :git => "https://github.com/cleanuidev/react-native-scanner.git", :tag => "#{s.version}" }
15
15
 
16
16
  s.source_files = "ios/**/*.{h,m,mm,swift,cpp}"
17
17
  s.private_header_files = "ios/**/*.h"
@@ -47,6 +47,9 @@ class CameraManager: NSObject, CameraControlProtocol {
47
47
  private var desiredTorchLevel: Float = 1.0
48
48
  private var desiredZoomLevel: CGFloat = 1.0
49
49
 
50
+ /// Observer for subject area change (refocus when scene changes, like native Camera app)
51
+ private var subjectAreaObserver: NSObjectProtocol?
52
+
50
53
  // MARK: - Initialization
51
54
 
52
55
  override init() {
@@ -81,9 +84,10 @@ class CameraManager: NSObject, CameraControlProtocol {
81
84
  self.isSessionRunning = true
82
85
  }
83
86
 
84
- // Apply any desired settings now that the session/device exists
87
+ // Apply settings after session is running (native camera app behavior)
85
88
  self.applyZoom()
86
89
  self.applyTorch()
90
+ self.setupFocusAndExposure()
87
91
 
88
92
  DispatchQueue.main.async {
89
93
  self.delegate?.cameraManagerDidStart(self)
@@ -94,6 +98,7 @@ class CameraManager: NSObject, CameraControlProtocol {
94
98
 
95
99
  /// Stop the camera session
96
100
  func stopCamera() {
101
+ unregisterSubjectAreaChangeObserver()
97
102
  sessionQueue.async { [weak self] in
98
103
  guard let self = self, self.isSessionRunning else { return }
99
104
 
@@ -126,6 +131,17 @@ class CameraManager: NSObject, CameraControlProtocol {
126
131
  }
127
132
  }
128
133
 
134
+ /// Set focus and exposure point of interest for better scanning of small barcodes/QR codes.
135
+ /// Call this after the camera has started (e.g. when focus area is known).
136
+ /// - Parameters:
137
+ /// - normalizedX: X in range 0...1 (0.5 = center)
138
+ /// - normalizedY: Y in range 0...1 (0.5 = center)
139
+ func setFocusPointOfInterest(normalizedX: CGFloat, normalizedY: CGFloat) {
140
+ sessionQueue.async { [weak self] in
141
+ self?.applyFocusPointOfInterest(normalizedX: normalizedX, normalizedY: normalizedY)
142
+ }
143
+ }
144
+
129
145
  /// Check if torch is available
130
146
  /// - Returns: True if torch is available
131
147
  func isTorchAvailable() -> Bool {
@@ -144,7 +160,14 @@ class CameraManager: NSObject, CameraControlProtocol {
144
160
  /// Configure the capture session
145
161
  private func configureCaptureSession() {
146
162
  captureSession.beginConfiguration()
147
- captureSession.sessionPreset = .high
163
+ // Prefer full HD for sharper small barcode/QR capture when supported (e.g. iPhone 15)
164
+ if captureSession.canSetSessionPreset(.hd1920x1080) {
165
+ captureSession.sessionPreset = .hd1920x1080
166
+ print("[CameraManager] Session preset: hd1920x1080")
167
+ } else {
168
+ captureSession.sessionPreset = .high
169
+ print("[CameraManager] Session preset: high")
170
+ }
148
171
 
149
172
  // Setup camera input
150
173
  guard setupCameraInput() else {
@@ -178,7 +201,16 @@ class CameraManager: NSObject, CameraControlProtocol {
178
201
  print("[CameraManager] No camera device available")
179
202
  return false
180
203
  }
181
-
204
+
205
+ // Bias autofocus towards near objects (ideal for barcodes/QR codes held close to camera).
206
+ // Must be configured before adding the device to an active session to be effective.
207
+ lockDeviceForConfiguration(device) { device in
208
+ if device.isAutoFocusRangeRestrictionSupported {
209
+ device.autoFocusRangeRestriction = .near
210
+ print("[CameraManager] βœ… Auto-focus range restriction set to NEAR")
211
+ }
212
+ }
213
+
182
214
  do {
183
215
  let input = try AVCaptureDeviceInput(device: device)
184
216
 
@@ -261,6 +293,103 @@ class CameraManager: NSObject, CameraControlProtocol {
261
293
  }
262
294
  }
263
295
 
296
+ // MARK: - Focus and Exposure (for small barcode/QR scanning)
297
+
298
+ /// Enable continuous autofocus and continuous auto-exposure (native Camera app behavior).
299
+ /// Applied after the session is running so the device is fully active.
300
+ private func setupFocusAndExposure() {
301
+ guard let device = currentDevice else { return }
302
+ lockDeviceForConfiguration(device) { device in
303
+ // Continuous autofocus: camera keeps refocusing so small codes stay sharp
304
+ if device.isFocusModeSupported(.continuousAutoFocus) {
305
+ device.focusMode = .continuousAutoFocus
306
+ print("[CameraManager] βœ… Continuous autofocus enabled")
307
+ } else if device.isFocusModeSupported(.autoFocus) {
308
+ device.focusMode = .autoFocus
309
+ print("[CameraManager] βœ… Auto focus enabled (continuous not supported)")
310
+ }
311
+ // Continuous auto-exposure: adapts to lighting for better small code readability
312
+ if device.isExposureModeSupported(.continuousAutoExposure) {
313
+ device.exposureMode = .continuousAutoExposure
314
+ print("[CameraManager] βœ… Continuous auto-exposure enabled")
315
+ }
316
+ // Default focus point at center (device coords); overridden by setFocusPointOfInterest
317
+ let center = CGPoint(x: 0.5, y: 0.5)
318
+ if device.isFocusPointOfInterestSupported && device.isFocusModeSupported(.continuousAutoFocus) {
319
+ device.focusPointOfInterest = center
320
+ }
321
+ if device.isExposurePointOfInterestSupported && device.isExposureModeSupported(.continuousAutoExposure) {
322
+ device.exposurePointOfInterest = center
323
+ }
324
+ // Native Camera behavior: refocus when scene changes (lighting, movement, etc.)
325
+ if device.isSubjectAreaChangeMonitoringEnabled == false {
326
+ device.isSubjectAreaChangeMonitoringEnabled = true
327
+ print("[CameraManager] βœ… Subject area change monitoring enabled")
328
+ }
329
+ }
330
+ // Observe scene changes and re-apply continuous focus (like native Camera app)
331
+ registerSubjectAreaChangeObserverIfNeeded()
332
+ }
333
+
334
+ /// When the scene changes, re-apply continuous focus so the camera refocuses.
335
+ private func registerSubjectAreaChangeObserverIfNeeded() {
336
+ guard subjectAreaObserver == nil else { return }
337
+ subjectAreaObserver = NotificationCenter.default.addObserver(
338
+ forName: .AVCaptureDeviceSubjectAreaDidChange,
339
+ object: currentDevice,
340
+ queue: .main
341
+ ) { [weak self] _ in
342
+ self?.sessionQueue.async {
343
+ self?.reapplyContinuousFocus()
344
+ }
345
+ }
346
+ print("[CameraManager] Subject area change observer registered")
347
+ }
348
+
349
+ private func unregisterSubjectAreaChangeObserver() {
350
+ if let observer = subjectAreaObserver {
351
+ NotificationCenter.default.removeObserver(observer)
352
+ subjectAreaObserver = nil
353
+ print("[CameraManager] Subject area change observer removed")
354
+ }
355
+ }
356
+
357
+ /// Re-apply continuous focus and exposure (called when scene changes).
358
+ private func reapplyContinuousFocus() {
359
+ guard let device = currentDevice else { return }
360
+ lockDeviceForConfiguration(device) { device in
361
+ if device.isFocusModeSupported(.continuousAutoFocus) {
362
+ device.focusMode = .continuousAutoFocus
363
+ }
364
+ if device.isExposureModeSupported(.continuousAutoExposure) {
365
+ device.exposureMode = .continuousAutoExposure
366
+ }
367
+ }
368
+ }
369
+
370
+ /// Set focus and exposure point of interest (e.g. center of focus area) so the camera
371
+ /// prioritizes that region. The inputs are expected to already be in device coordinates
372
+ /// (0,0 = top-left, 1,1 = bottom-right) as returned by AVCaptureVideoPreviewLayer helpers.
373
+ private func applyFocusPointOfInterest(normalizedX: CGFloat, normalizedY: CGFloat) {
374
+ guard let device = currentDevice else { return }
375
+ let devicePoint = CGPoint(x: normalizedX, y: normalizedY)
376
+ lockDeviceForConfiguration(device) { device in
377
+ if device.isFocusPointOfInterestSupported {
378
+ device.focusPointOfInterest = devicePoint
379
+ if device.isFocusModeSupported(.continuousAutoFocus) {
380
+ device.focusMode = .continuousAutoFocus
381
+ }
382
+ }
383
+ if device.isExposurePointOfInterestSupported {
384
+ device.exposurePointOfInterest = devicePoint
385
+ if device.isExposureModeSupported(.continuousAutoExposure) {
386
+ device.exposureMode = .continuousAutoExposure
387
+ }
388
+ }
389
+ print("[CameraManager] Focus point of interest set (view: \(normalizedX), \(normalizedY) β†’ device: \(devicePoint.x), \(devicePoint.y))")
390
+ }
391
+ }
392
+
264
393
  /// Setup video output
265
394
  /// - Returns: True if setup successful
266
395
  @discardableResult
@@ -296,12 +425,18 @@ class CameraManager: NSObject, CameraControlProtocol {
296
425
  /// Get the default camera device (back camera)
297
426
  /// - Returns: The camera device or nil
298
427
  private func getDefaultCameraDevice() -> AVCaptureDevice? {
299
- // Prefer a BACK camera that supports torch.
428
+ // Prefer the physical wide‑angle back camera for barcode scanning.
429
+ // Using the virtual multi‑camera (e.g. builtInTripleCamera) on newer iPhones
430
+ // can cause macro / close‑range focus issues for small barcodes.
431
+ if let wideBack = AVCaptureDevice.default(.builtInWideAngleCamera, for: .video, position: .back) {
432
+ print("[CameraManager] βœ… Using back wide‑angle camera: \(wideBack.localizedName)")
433
+ return wideBack
434
+ }
435
+
436
+ // Fallback: look for any other back camera with torch (dual, telephoto, etc.)
300
437
  let deviceTypes: [AVCaptureDevice.DeviceType] = [
301
- .builtInTripleCamera,
302
438
  .builtInDualCamera,
303
439
  .builtInDualWideCamera,
304
- .builtInWideAngleCamera,
305
440
  .builtInTelephotoCamera,
306
441
  .builtInUltraWideCamera,
307
442
  ]
@@ -313,7 +448,7 @@ class CameraManager: NSObject, CameraControlProtocol {
313
448
  )
314
449
 
315
450
  if let torchBack = discovery.devices.first(where: { $0.hasTorch }) {
316
- print("[CameraManager] βœ… Using back camera with torch: \(torchBack.localizedName)")
451
+ print("[CameraManager] βœ… Using fallback back camera with torch: \(torchBack.localizedName)")
317
452
  return torchBack
318
453
  }
319
454
 
@@ -371,6 +506,7 @@ class CameraManager: NSObject, CameraControlProtocol {
371
506
  }
372
507
 
373
508
  deinit {
509
+ unregisterSubjectAreaChangeObserver()
374
510
  stopCamera()
375
511
  print("[CameraManager] Deinitialized")
376
512
  }
@@ -146,6 +146,8 @@ class ScannerViewImpl: UIView {
146
146
  @objc func configureFocusArea(_ config: [String: Any]) {
147
147
  focusAreaConfig = FocusAreaConfig.from(dict: config)
148
148
  focusAreaOverlay.updateFocusArea(config: focusAreaConfig)
149
+ // Update camera focus point to focus area center for better small barcode scanning
150
+ setupFocusPointOfInterest()
149
151
  print("[ScannerViewImpl] Focus area configured")
150
152
  }
151
153
 
@@ -520,10 +522,38 @@ extension ScannerViewImpl: CameraManagerDelegate {
520
522
  // Check bounds multiple times with delay to catch layout
521
523
  self.attemptToAddPreviewLayer(attempt: 0)
522
524
 
525
+ // Set focus point of interest (center or focus area center) for better small barcode/QR scanning
526
+ self.setupFocusPointOfInterest()
527
+
523
528
  self.emitLoadEvent(success: true)
524
529
  }
525
530
  }
526
531
 
532
+ /// Set the camera focus point of interest to the focus area center (or screen center)
533
+ /// so continuous autofocus prioritizes that region and small barcodes/QR codes stay sharp.
534
+ private func setupFocusPointOfInterest() {
535
+ guard bounds.width > 0, bounds.height > 0 else { return }
536
+ guard let previewLayer = self.previewLayer else { return }
537
+
538
+ // 1) Get center point in view coordinates (either focus area center or view center)
539
+ let centerPointInView: CGPoint
540
+ if focusAreaConfig.enabled, let focusRect = focusAreaOverlay.getFocusAreaFrame() {
541
+ centerPointInView = CGPoint(x: focusRect.midX, y: focusRect.midY)
542
+ } else {
543
+ centerPointInView = CGPoint(x: bounds.midX, y: bounds.midY)
544
+ }
545
+
546
+ // 2) Convert from view/layer space into device focus coordinates using AVFoundation helper
547
+ let pointInLayer = previewLayer.convert(centerPointInView, from: self.layer)
548
+ let devicePoint = previewLayer.captureDevicePointConverted(fromLayerPoint: pointInLayer)
549
+
550
+ // 3) Pass device-normalized coordinates (0,0 top-left, 1,1 bottom-right) to camera manager
551
+ cameraManager.setFocusPointOfInterest(
552
+ normalizedX: devicePoint.x,
553
+ normalizedY: devicePoint.y
554
+ )
555
+ }
556
+
527
557
  private func attemptToAddPreviewLayer(attempt: Int) {
528
558
  guard !isPreviewLayerAdded else { return }
529
559
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cleanuidev/react-native-scanner",
3
- "version": "1.0.0-beta.3",
3
+ "version": "1.0.0-beta.4",
4
4
  "description": "High-performance native barcode and QR code scanner for React Native. Scan barcodes in configurable target areas for precise detection. Built with CameraX & ML Kit (Android) and AVFoundation & Vision (iOS).",
5
5
  "main": "./lib/module/index.js",
6
6
  "types": "./lib/typescript/src/index.d.ts",