Conversation
…ocessing features
…guide, and enhance example app
WalkthroughThe PR standardizes automatic image preprocessing across the API and implementation, removes user-configurable preprocessing options, updates documentation and example app accordingly, adds a new iOS native scanner with Vision-based processing, refactors JS glue, and adjusts tooling/configs and dependencies. Changes
Sequence Diagram(s)sequenceDiagram
autonumber
actor User
participant App as Example App (JS)
participant API as ImageCodeScanner (JS)
participant Native as Native Module
participant iOS as iOS Vision Pipeline
User->>App: Tap "Scan Image"
App->>API: scan({ path, formats })
note right of API: Preprocessing always enabled (flags fixed)
API->>Native: scanFromPath(path, formats, { enhanceContrast, grayscale, rotations })
par Prepare variants
Native->>iOS: Load original image
iOS-->>Native: Original
Native->>iOS: Grayscale/Contrast
iOS-->>Native: Enhanced variants
Native->>iOS: Rotations (0°, 90°, 180°, 270°)
iOS-->>Native: Rotated variants
end
loop For each variant
Native->>iOS: VNDetectBarcodesRequest(formats)
iOS-->>Native: Results or none
end
alt Codes found
Native-->>API: Resolve [payloads]
API-->>App: Results
App-->>User: Show results list
else None found or error
Native->>iOS: Fallback QR (CIDetector)
iOS-->>Native: Results or none
alt Any results
Native-->>API: Resolve [payloads]
API-->>App: Results
else
Native-->>API: Resolve []
API-->>App: No results
App-->>User: Notify no codes
end
end
Estimated code review effort🎯 4 (Complex) | ⏱️ ~60 minutes Poem
Tip 🔌 Remote MCP (Model Context Protocol) integration is now available!Pro plan users can now connect to remote MCP servers from the Integrations page. Connect with popular remote MCPs such as Notion and Linear to add more context to your reviews and chats. ✨ Finishing Touches
🧪 Generate unit tests
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. 🪧 TipsChatThere are 3 ways to chat with CodeRabbit:
SupportNeed help? Create a ticket on our support page for assistance with any issues or questions. CodeRabbit Commands (Invoked using PR/Issue comments)Type Other keywords and placeholders
CodeRabbit Configuration File (
|
There was a problem hiding this comment.
Actionable comments posted: 10
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
README.md (1)
372-379: Example app bullets reference “real-time preprocessing option toggles,” which were removed.Align the bullets with the new UI that only shows informational text.
-- Real-time preprocessing option toggles +- Automatic preprocessing (no toggles required)
🧹 Nitpick comments (26)
.gitignore (1)
71-71: Do not ignore yarn.lock; commit it for reproducible installs/CI.For Yarn 3 workspaces, keeping the lockfile in VCS avoids resolution drift and speeds CI. Recommend removing this ignore.
- yarn.lockscripts/validate-package.js (2)
6-8: Nit: single-line log is fine; current multi-line call is noisy.Functionally identical; consider reverting to a single line to keep the script compact.
-console.log( - '🔍 Validating React Native Image Code Scanner package structure...\n' -); +console.log('🔍 Validating React Native Image Code Scanner package structure...\n');
86-91: Avoid warning on lib/ — it’s expected after bob build; warn only if tracked.lib is a build artifact but expected locally; current check creates false positives. Either drop lib from unwantedDirs or only warn if files under lib are tracked by git.
-const unwantedDirs = ['modules', 'build', 'lib', '.turbo']; +const unwantedDirs = ['modules', 'build', '.turbo'];Optional: add a tracked-files check.
+const { execSync } = require('child_process'); @@ // Check for unwanted files -const unwantedDirs = ['modules', 'build', 'lib', '.turbo']; +const unwantedDirs = ['modules', 'build', '.turbo']; unwantedDirs.forEach((dir) => { if (fs.existsSync(path.join(__dirname, '..', dir))) { warnings.push(`⚠️ Found directory that should be cleaned: ${dir}`); } }); + +// Warn only if lib has tracked files (don’t commit build artifacts) +try { + const tracked = execSync('git ls-files lib').toString().trim(); + if (tracked) warnings.push('⚠️ lib contains tracked files; avoid committing build artifacts.'); +} catch {}package.json (1)
35-50: Run the validator in prepublishOnly (and add a script target).Helps catch packaging issues before publish.
"scripts": { @@ - "prepublishOnly": "yarn clean && yarn typecheck && yarn lint && yarn test && yarn build", + "validate": "node scripts/validate-package.js", + "prepublishOnly": "yarn clean && yarn typecheck && yarn lint && yarn test && yarn build && node scripts/validate-package.js",example/README.md (1)
97-99: Docs now claim “automatic,” but other sections still mention toggles — align messagingThese bullets are good. However, earlier sections still promise “Preprocessing Options,” “Real-time Configuration,” and step 3 says “Configure Preprocessing,” which conflicts with “automatic” behavior.
- Update Features (Lines 10–12) to remove “Preprocessing Options” and “Real-time Configuration.”
- Update Usage (Lines 91–93) to remove “Configure Preprocessing.”
- Update Troubleshooting (Lines 121–124) to remove “Try enabling preprocessing options.”
- Update Development (Lines 156–158) to remove “Update preprocessing options in the state.”
I can draft a PR-ready edit if you want.
CHANGELOG.md (2)
10-22: Release date and “automatic preprocessing” look good — add a verification note and tighten phrasing
- Keep 2025-08-27; before publishing, verify the tag URL resolves (Line 50).
- Minor copy polish for consistency:
- “Multiple rotation attempts” → “Multiple rotation attempts: 0°, 90°, 180°, 270°.”
- “Ultra-simple API – just pass image path and formats; preprocessing is automatic.”
If you’d like, I’ll push wording tweaks.
31-33: Combine these two bullets to avoid repetitionConsider: “Automatic preprocessing enabled by default (contrast, grayscale, rotations) with smart retry logic.”
eslint.config.mjs (2)
18-19: Ignoring example/ may hide issues in the showcased app**Consider dropping 'example/**' from ignores so the example app stays linted; it’s often the first integration point users see.
27-33: Add prettier “turn-off-conflicts” to avoid rule clashesWith flat config, keep the plugin rule, but also disable conflicting stylistic rules:
...fixupConfigRules(compat.extends('@react-native')), + ...fixupConfigRules(compat.extends('prettier')),This mirrors eslint-config-prettier’s effect under FlatCompat.
ios/ImageCodeScanner.swift (2)
181-185: Use the Core Image QR fallback before returning empty resultsCall the existing fallback when Vision finds nothing:
- print("ImageCodeScanner iOS - No barcodes found after trying all preprocessing options") - safeResolve([]) + print("ImageCodeScanner iOS - No Vision results; trying CI QR fallback") + tryQRCodeFallback(image: originalImage, safeResolve: safeResolve, safeReject: safeReject)
83-136: Prefer RCTLog over print for RN-friendly loggingSwap print(...) with RCTLogInfo/RCTLogWarn/RCTLogError to make logs consistent with RN tooling and filters.
src/NativeImageCodeScanner.ts (1)
5-13: Type sync check across platforms; otherwise looks good
- Ensure Android and iOS both accept the same options keys (enhanceContrast, convertToGrayscale, tryRotations) even if currently ignored.
- If the public JS API always uses automatic preprocessing internally, consider marking options as internal-only or making it optional.
Optional tightenings:
- formats: readonly string[]
- Replace string union with an enum for known formats to catch typos at compile time.
I can push the types refinement if desired.
COMPATIBILITY.md (6)
3-20: Stop forecasting unreleased RN versions; mark “tested up to” with a date.Claiming support for 0.80.x+ (“Ready when released”) can mislead. Prefer “Tested up to RN as of 2025-08-27.” Remove the unreleased row or mark as TBD.
Apply:
-| 0.80.x+ | 🔜 0.1.x | New Architecture | Ready when released | +| (TBD) | — | — | Pending upstream release |
53-64: Reword Android requirements to align with RN templates and avoid stale numbers.Hardcoding 33+ may drift. Recommend “use the RN template’s compile/target SDK,” and list ML Kit as the only required dep for image scanning (CameraX is optional).
- - **Target SDK**: 33+ (Android 13+) - - **Compile SDK**: 33+ - - **Kotlin**: 1.6.0+ - - **Gradle**: 7.0+ - - **Android Gradle Plugin**: 7.0+ - - **Dependencies**: - - Google ML Kit Barcode Scanning: 17.3.0+ - - AndroidX Camera Core: 1.3.1+ + - **Target/Compile SDK**: Match the React Native template for your RN version + - **Kotlin/Gradle/AGP**: Match the React Native template (avoid overriding in library projects) + - **Dependencies**: + - Required: Google ML Kit Barcode Scanning (play-services-mlkit-barcode-scanning) + - Optional: CameraX (only if you add live camera scanning in the future)If CameraX isn’t actually used anywhere, please remove it from docs entirely to prevent confusion.
88-109: New Architecture enablement instructions: add a caution for mixed-arch pods/gradle caches.A one-liner note to “clean pods/Gradle” reduces common migration failures.
cd ios && RCT_NEW_ARCH_ENABLED=1 pod install +## If switching architectures, clean caches: +## iOS: rm -rf ~/Library/Developer/Xcode/DerivedData && pod deintegrate && pod install +## Android: ./gradlew clean
146-151: Use a neutrally stable init command and avoid pinning a possibly non-existent patch.Pinning “0.79.2” may break when that tag isn’t available locally.
-npx react-native init TestApp --version 0.79.2 +npx react-native@latest init TestApp --version <desired RN version>Confirm the exact RN versions you validated and replace with those numbers.
175-180: Types guidance likely outdated—RN ships its own types.Advising to install @types/react-native can conflict with RN’s bundled types for modern versions.
- npm install --save-dev @types/react@^18.0.0 @types/react-native@^0.72.0 + npm install --save-dev @types/react@^18 + # React Native provides its own TypeScript types; no extra @types/react-native needed for modern RN.Please verify the minimum RN where bundled types are reliable in your test matrix and reflect that here.
201-207: Add a “Last verified” note to Resources.Helps readers interpret version tables without guessing freshness.
- [Package Changelog](./CHANGELOG.md) + - [Package Changelog](./CHANGELOG.md) + +Last verified against React Native <version> on 2025-08-27.README.md (3)
144-166: Object.values(BarcodeFormat) may not type-check without a cast.In TS, Object.values on a string enum yields string[]. Cast to BarcodeFormat[] to satisfy ScanOptions.
- formats: Object.values(BarcodeFormat), // All supported formats + formats: Object.values(BarcodeFormat) as BarcodeFormat[], // All supported formats
306-314: Terminology drift: “Smart Retry Logic” contradicts the earlier removal of user-configurable preprocessing.Consider renaming to “Automatic retry strategy” and ensure it’s clearly non-configurable.
-- **Smart Retry Logic**: If initial scan fails, automatically tries with different preprocessing techniques +- **Automatic retry strategy**: If initial scan fails, the scanner automatically retries with different preprocessing techniques
53-60: Compatibility table claims “0.80.x+ fully supported (when released).”Mirror COMPATIBILITY.md guidance and avoid promising future compatibility.
-| 0.80.x+ | ✅ 0.1.x | Fully Supported (when released) | +| (TBD) | — | Pending upstream release |Add “Tested up to RN as of 2025-08-27” below the table.
src/index.tsx (1)
31-35: Type nativeOptions and consider lifting to a const export for reuse/testing.Improves readability and prevents drift with native signature.
- const nativeOptions = { + type NativeOptions = { + enhanceContrast: boolean; + convertToGrayscale: boolean; + tryRotations: boolean; + }; + const nativeOptions: NativeOptions = { enhanceContrast: true, convertToGrayscale: true, tryRotations: true, };Confirm NativeImageCodeScanner.scanFromPath(path, string[], options) matches this shape exactly across iOS/Android.
example/src/App.tsx (3)
21-25: Remove unused field from ScanResult.preprocessingUsed isn’t set or displayed.
-interface ScanResult { - data: string[]; - time: number; - preprocessingUsed?: string; -} +interface ScanResult { + data: string[]; + time: number; +}
213-217: Disable “Scan” when no formats are selected.Extra guard to avoid a no-op call.
- disabled={!selectedImage || isScanning} + disabled={!selectedImage || isScanning || selectedFormats.length === 0}
44-58: Ask only for necessary permissions per action.Requesting camera access when opening the gallery (and vice versa) adds friction.
Refactor requestPermissions to accept the action and request only the relevant permission. I can draft a patch if you want it integrated now.
example/App.tsx (1)
25-32: Drop unused “enabled” hints in BARCODE_FORMATS.They’re never read; state drives selection.
-const BARCODE_FORMATS = [ - { key: BarcodeFormat.QR_CODE, label: 'QR Code', enabled: true }, - { key: BarcodeFormat.CODE_128, label: 'Code 128', enabled: false }, - { key: BarcodeFormat.CODE_39, label: 'Code 39', enabled: false }, - { key: BarcodeFormat.EAN_13, label: 'EAN-13', enabled: false }, - { key: BarcodeFormat.PDF_417, label: 'PDF417', enabled: false }, - { key: BarcodeFormat.DATA_MATRIX, label: 'Data Matrix', enabled: false }, -]; +const BARCODE_FORMATS = [ + { key: BarcodeFormat.QR_CODE, label: 'QR Code' }, + { key: BarcodeFormat.CODE_128, label: 'Code 128' }, + { key: BarcodeFormat.CODE_39, label: 'Code 39' }, + { key: BarcodeFormat.EAN_13, label: 'EAN-13' }, + { key: BarcodeFormat.PDF_417, label: 'PDF417' }, + { key: BarcodeFormat.DATA_MATRIX, label: 'Data Matrix' }, +];
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
💡 Knowledge Base configuration:
- MCP integration is disabled by default for public repositories
- Jira integration is disabled by default for public repositories
- Linear integration is disabled by default for public repositories
You can enable these sources in your CodeRabbit configuration.
📒 Files selected for processing (15)
.gitignore(1 hunks)CHANGELOG.md(2 hunks)COMPATIBILITY.md(1 hunks)README.md(11 hunks)eslint.config.mjs(1 hunks)example/App.tsx(1 hunks)example/README.md(1 hunks)example/babel.config.js(1 hunks)example/package.json(1 hunks)example/src/App.tsx(9 hunks)ios/ImageCodeScanner.swift(1 hunks)package.json(2 hunks)scripts/validate-package.js(4 hunks)src/NativeImageCodeScanner.ts(1 hunks)src/index.tsx(1 hunks)
🧰 Additional context used
🧬 Code graph analysis (2)
ios/ImageCodeScanner.swift (1)
android/src/main/java/com/imagecodescanner/ImageCodeScannerModule.kt (4)
convertToGrayscale(77-93)name(19-265)enhanceContrast(48-75)scanFromPath(101-260)
example/App.tsx (1)
example/src/App.tsx (1)
App(36-248)
🪛 LanguageTool
COMPATIBILITY.md
[grammar] ~23-~23: There might be a mistake here.
Context: ...sions | React Version | Compatibility | |--------------|---------------| | 17.x ...
(QB_NEW_EN)
[grammar] ~24-~24: There might be a mistake here.
Context: ...ility | |--------------|---------------| | 17.x | ✅ Supported | | 18.x ...
(QB_NEW_EN)
[grammar] ~25-~25: There might be a mistake here.
Context: ...-------| | 17.x | ✅ Supported | | 18.x | ✅ Supported (Recommende...
(QB_NEW_EN)
[grammar] ~26-~26: There might be a mistake here.
Context: ....x | ✅ Supported (Recommended) | | 19.x | ✅ Supported (Beta) | #...
(QB_NEW_EN)
[grammar] ~31-~31: There might be a mistake here.
Context: ...eact Native | Package Version | Status | |----------|-------------|--------------...
(QB_NEW_EN)
[grammar] ~32-~32: There might be a mistake here.
Context: ...------------|-----------------|--------| | SDK 49 | 0.72.x | ✅ 0.1.x ...
(QB_NEW_EN)
[grammar] ~33-~33: There might be a mistake here.
Context: ... | Supported (requires prebuild) | | SDK 50 | 0.73.x | ✅ 0.1.x ...
(QB_NEW_EN)
[grammar] ~34-~34: There might be a mistake here.
Context: ... | Supported (requires prebuild) | | SDK 51 | 0.74.x | ✅ 0.1.x ...
(QB_NEW_EN)
[grammar] ~35-~35: There might be a mistake here.
Context: ... | Supported (requires prebuild) | | SDK 52 | 0.79.x | ✅ 0.1.x ...
(QB_NEW_EN)
[grammar] ~44-~44: There might be a mistake here.
Context: ...rements - Minimum iOS Version: 13.4 - Xcode: 14.0 or higher - Swift: 5.0...
(QB_NEW_EN)
[grammar] ~45-~45: There might be a mistake here.
Context: ...sion**: 13.4 - Xcode: 14.0 or higher - Swift: 5.0 or higher - **Frameworks Re...
(QB_NEW_EN)
[grammar] ~46-~46: There might be a mistake here.
Context: ...4.0 or higher - Swift: 5.0 or higher - Frameworks Required: - Vision Framew...
(QB_NEW_EN)
[grammar] ~47-~47: There might be a mistake here.
Context: ...5.0 or higher - Frameworks Required: - Vision Framework (iOS 11+) - Core Imag...
(QB_NEW_EN)
[grammar] ~48-~48: There might be a mistake here.
Context: ...quired**: - Vision Framework (iOS 11+) - Core Image (iOS 5+) - UIKit (iOS 2+) ...
(QB_NEW_EN)
[grammar] ~49-~49: There might be a mistake here.
Context: ...mework (iOS 11+) - Core Image (iOS 5+) - UIKit (iOS 2+) - Core Graphics (iOS 2+...
(QB_NEW_EN)
[grammar] ~50-~50: There might be a mistake here.
Context: ...- Core Image (iOS 5+) - UIKit (iOS 2+) - Core Graphics (iOS 2+) ### Android Requ...
(QB_NEW_EN)
[grammar] ~55-~55: There might be a mistake here.
Context: ...Minimum SDK**: 21 (Android 5.0 Lollipop) - Target SDK: 33+ (Android 13+) - **Comp...
(QB_NEW_EN)
[grammar] ~56-~56: There might be a mistake here.
Context: ...pop) - Target SDK: 33+ (Android 13+) - Compile SDK: 33+ - Kotlin: 1.6.0+ ...
(QB_NEW_EN)
[grammar] ~57-~57: There might be a mistake here.
Context: ...33+ (Android 13+) - Compile SDK: 33+ - Kotlin: 1.6.0+ - Gradle: 7.0+ - **...
(QB_NEW_EN)
[grammar] ~58-~58: There might be a mistake here.
Context: ...Compile SDK*: 33+ - Kotlin: 1.6.0+ - Gradle: 7.0+ - **Android Gradle Plugin...
(QB_NEW_EN)
[grammar] ~59-~59: There might be a mistake here.
Context: ... - Kotlin: 1.6.0+ - Gradle: 7.0+ - Android Gradle Plugin: 7.0+ - **Depend...
(QB_NEW_EN)
[grammar] ~60-~60: There might be a mistake here.
Context: ...: 7.0+ - Android Gradle Plugin: 7.0+ - Dependencies: - Google ML Kit Barcod...
(QB_NEW_EN)
[grammar] ~61-~61: There might be a mistake here.
Context: ...radle Plugin**: 7.0+ - Dependencies: - Google ML Kit Barcode Scanning: 17.3.0+ ...
(QB_NEW_EN)
[grammar] ~62-~62: There might be a mistake here.
Context: ... Google ML Kit Barcode Scanning: 17.3.0+ - AndroidX Camera Core: 1.3.1+ ### Node.j...
(QB_NEW_EN)
[grammar] ~67-~67: There might be a mistake here.
Context: ... Node.js: >=18.0.0 (LTS recommended) - npm: >=8.0.0 - Yarn: >=1.22.0 or >...
(QB_NEW_EN)
[grammar] ~68-~68: There might be a mistake here.
Context: ...0.0 (LTS recommended) - npm: >=8.0.0 - Yarn: >=1.22.0 or >=3.0.0 (Berry) ## ...
(QB_NEW_EN)
[grammar] ~155-~155: There might be a mistake here.
Context: ... Testing The library is tested against: - React Native 0.70.x (Old Architecture) -...
(QB_NEW_EN)
[grammar] ~156-~156: There might be a mistake here.
Context: ...- React Native 0.70.x (Old Architecture) - React Native 0.75.x (Both Architectures)...
(QB_NEW_EN)
[grammar] ~157-~157: There might be a mistake here.
Context: ...React Native 0.75.x (Both Architectures) - React Native 0.79.x (New Architecture) -...
(QB_NEW_EN)
[grammar] ~158-~158: There might be a mistake here.
Context: ...- React Native 0.79.x (New Architecture) - Latest React Native release ## Known Is...
(QB_NEW_EN)
[grammar] ~184-~184: There might be a mistake here.
Context: ... Latest 3 minor versions of React Native - Security Updates: Latest 6 minor versi...
(QB_NEW_EN)
[grammar] ~185-~185: There might be a mistake here.
Context: ... Latest 6 minor versions of React Native - Best Effort: Older versions on case-by...
(QB_NEW_EN)
[grammar] ~186-~186: There might be a mistake here.
Context: ...ative - Best Effort: Older versions on case-by-case basis ## Reporting Compat...
(QB_NEW_EN)
[grammar] ~194-~194: There might be a mistake here.
Context: ...nner/issues) 3. Create a new issue with: - React Native version - Package versio...
(QB_NEW_EN)
[grammar] ~195-~195: There might be a mistake here.
Context: ...ew issue with: - React Native version - Package version - Platform (iOS/Andro...
(QB_NEW_EN)
[grammar] ~196-~196: There might be a mistake here.
Context: ...eact Native version - Package version - Platform (iOS/Android) - Architecture...
(QB_NEW_EN)
[grammar] ~197-~197: There might be a mistake here.
Context: ...kage version - Platform (iOS/Android) - Architecture (Old/New) - Error messag...
(QB_NEW_EN)
[grammar] ~198-~198: There might be a mistake here.
Context: ...iOS/Android) - Architecture (Old/New) - Error messages/logs ## Resources - [Re...
(QB_NEW_EN)
README.md
[grammar] ~55-~55: There might be a mistake here.
Context: ...ive Version | Package Version | Status | |---------------------|-----------------...
(QB_NEW_EN)
[grammar] ~56-~56: There might be a mistake here.
Context: ...------------|-----------------|--------| | 0.70.x - 0.74.x | ✅ 0.1.x | ...
(QB_NEW_EN)
[grammar] ~57-~57: There might be a mistake here.
Context: ... | ✅ 0.1.x | Fully Supported | | 0.75.x - 0.79.x | ✅ 0.1.x | ...
(QB_NEW_EN)
[grammar] ~58-~58: There might be a mistake here.
Context: ...Supported (including New Architecture) | | 0.80.x+ | ✅ 0.1.x | ...
(QB_NEW_EN)
[grammar] ~61-~61: There might be a mistake here.
Context: ...rted (when released) | ### Requirements - React Native: >=0.70.0 - React: >=...
(QB_NEW_EN)
[grammar] ~62-~62: There might be a mistake here.
Context: ...equirements - React Native: >=0.70.0 - React: >=17.0.0 - iOS: 13.4+ - **A...
(QB_NEW_EN)
[grammar] ~63-~63: There might be a mistake here.
Context: ...Native**: >=0.70.0 - React: >=17.0.0 - iOS: 13.4+ - Android: minSdkVersio...
(QB_NEW_EN)
[grammar] ~64-~64: There might be a mistake here.
Context: ...0 - React: >=17.0.0 - iOS: 13.4+ - Android: minSdkVersion 21+ - Node:...
(QB_NEW_EN)
[grammar] ~65-~65: There might be a mistake here.
Context: ...: 13.4+ - Android: minSdkVersion 21+ - Node: >=18 ## Installation ```bash n...
(QB_NEW_EN)
[grammar] ~172-~172: There might be a mistake here.
Context: .... Original Image: First scan attempt 2. Grayscale Conversion: Improves detecti...
(QB_NEW_EN)
[grammar] ~173-~173: There might be a mistake here.
Context: ...mproves detection in colored backgrounds 3. Contrast Enhancement: Better recogniti...
(QB_NEW_EN)
[grammar] ~174-~174: There might be a mistake here.
Context: ...etter recognition in low-contrast images 4. Rotation Attempts: Tries 0°, 90°, 180°...
(QB_NEW_EN)
[grammar] ~313-~313: There might be a mistake here.
Context: ... automatically - Smart Retry Logic: If initial scan fails, automatically tries...
(QB_NEW_EN)
CHANGELOG.md
[grammar] ~17-~17: There might be a mistake here.
Context: ...preprocessing** for optimal recognition: - Contrast enhancement - Grayscale conve...
(QB_NEW_EN)
[grammar] ~18-~18: There might be a mistake here.
Context: ...al recognition: - Contrast enhancement - Grayscale conversion - Multiple rotati...
(QB_NEW_EN)
[grammar] ~19-~19: There might be a mistake here.
Context: ...ast enhancement - Grayscale conversion - Multiple rotation attempts (0°, 90°, 180...
(QB_NEW_EN)
[grammar] ~20-~20: There might be a mistake here.
Context: ... rotation attempts (0°, 90°, 180°, 270°) - Ultra-simple API - just pass image pat...
(QB_NEW_EN)
[grammar] ~31-~31: There might be a mistake here.
Context: ...ng** enabled by default for best results - Smart retry logic with multiple image en...
(QB_NEW_EN)
[grammar] ~32-~32: There might be a mistake here.
Context: ...th multiple image enhancement techniques - No additional setup required for Android...
(QB_NEW_EN)
[grammar] ~33-~33: There might be a mistake here.
Context: ...No additional setup required for Android - Minimal iOS setup with just pod install ...
(QB_NEW_EN)
[grammar] ~34-~34: There might be a mistake here.
Context: ... Minimal iOS setup with just pod install - Expo integration with proper prebuild ...
(QB_NEW_EN)
[grammar] ~35-~35: There might be a mistake here.
Context: ...egration** with proper prebuild workflow - Modern example app using Expo Image Pi...
(QB_NEW_EN)
[grammar] ~36-~36: There might be a mistake here.
Context: ...** using Expo Image Picker and StatusBar - Simplified API - just pass image path ...
(QB_NEW_EN)
[grammar] ~37-~37: There might be a mistake here.
Context: ...API** - just pass image path and formats - Performance metrics and timing measure...
(QB_NEW_EN)
[grammar] ~40-~40: There might be a mistake here.
Context: ...g measurements ### Example App Features - Modern Expo-based example application - ...
(QB_NEW_EN)
[grammar] ~41-~41: There might be a mistake here.
Context: ... - Modern Expo-based example application - Barcode format selection UI with real-ti...
(QB_NEW_EN)
[grammar] ~42-~42: There might be a mistake here.
Context: ...rmat selection UI with real-time toggles - Automatic preprocessing info with option...
(QB_NEW_EN)
[grammar] ~43-~43: There might be a mistake here.
Context: ...essing info with optional disable switch - Improved error handling and user feedbac...
(QB_NEW_EN)
🔇 Additional comments (7)
example/babel.config.js (1)
1-6: Babel config is minimal and correct for Expo.Flat, cached config with babel-preset-expo is appropriate. No issues.
eslint.config.mjs (1)
16-21: Shareable ESLint config identifier ‘@react-native’ resolves correctlypackage.json declares the scoped package
@react-native/eslint-config(e.g."@react-native/eslint-config": "^0.79.2"), socompat.extends('@react-native')will successfully load that shareable config. No change needed.README.md (3)
28-32: Great clarity on automatic preprocessing.The shift to automatic preprocessing is clearly explained and consistent with code changes.
168-178: Minor: tighten “How It Works” phrasing to reflect early-exit behavior.You already do this—good callout. No change needed.
194-199: Comment aligns with API change.“Automatic preprocessing is enabled by default” is consistent with src/index.tsx and native flow.
example/App.tsx (2)
325-333: Confirm RN “gap” support in your target versions.gap in React Native styles is relatively new; ensure it’s supported across your documented RN matrix or replace with margins.
If needed, I can provide a small fallback style util.
222-233: Nice UX: prevents accidental double-tap by showing a spinner.Clean pattern; no changes needed.
| "expo": "~52.0.0", | ||
| "expo-status-bar": "~2.0.0", | ||
| "expo-image-picker": "~16.0.0", | ||
| "expo-permissions": "~18.0.0", | ||
| "expo-image-picker": "~16.0.3", | ||
| "expo-status-bar": "~2.0.1", | ||
| "react": "18.3.1", | ||
| "react-native": "0.76.3", | ||
| "react-native": "0.79.2", | ||
| "react-native-image-code-scanner": "file:../" | ||
| }, |
There was a problem hiding this comment.
Expo SDK-managed projects shouldn’t pin react-native; let Expo resolve it.
Pinning RN (0.79.2) can conflict with Expo SDK 52. Remove react-native here and rely on Expo (expo install/prebuild) to select the compatible version.
"dependencies": {
"expo": "~52.0.0",
"expo-image-picker": "~16.0.3",
"expo-status-bar": "~2.0.1",
"react": "18.3.1",
- "react-native": "0.79.2",
"react-native-image-code-scanner": "file:../"
},📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| "expo": "~52.0.0", | |
| "expo-status-bar": "~2.0.0", | |
| "expo-image-picker": "~16.0.0", | |
| "expo-permissions": "~18.0.0", | |
| "expo-image-picker": "~16.0.3", | |
| "expo-status-bar": "~2.0.1", | |
| "react": "18.3.1", | |
| "react-native": "0.76.3", | |
| "react-native": "0.79.2", | |
| "react-native-image-code-scanner": "file:../" | |
| }, | |
| "dependencies": { | |
| "expo": "~52.0.0", | |
| "expo-image-picker": "~16.0.3", | |
| "expo-status-bar": "~2.0.1", | |
| "react": "18.3.1", | |
| "react-native-image-code-scanner": "file:../" | |
| }, |
🤖 Prompt for AI Agents
In example/package.json around lines 19 to 25 the dependency list pins
"react-native": "0.79.2", which conflicts with Expo SDK-managed projects; remove
the "react-native" entry from package.json so Expo can manage the RN version,
then run the appropriate Expo tooling (expo install or expo prebuild) to let
Expo resolve and install the compatible react-native version and update the
lockfile; do not manually re-add a react-native version.
| value={selectedFormats.includes(format.key)} | ||
| onValueChange={(value) => { | ||
| if (value) { | ||
| setSelectedFormats(prev => [...prev, format.key]); | ||
| setSelectedFormats((prev) => [...prev, format.key]); | ||
| } else { | ||
| setSelectedFormats(prev => prev.filter(f => f !== format.key)); | ||
| setSelectedFormats((prev) => | ||
| prev.filter((f) => f !== format.key) | ||
| ); | ||
| } | ||
| }} | ||
| /> |
There was a problem hiding this comment.
🛠️ Refactor suggestion
Prevent deselecting the last barcode format.
Currently users can end up with formats: [], which yields no results.
- <Switch
+ <Switch
value={selectedFormats.includes(format.key)}
onValueChange={(value) => {
if (value) {
- setSelectedFormats((prev) => [...prev, format.key]);
+ setSelectedFormats((prev) =>
+ prev.includes(format.key) ? prev : [...prev, format.key]
+ );
} else {
- setSelectedFormats((prev) =>
- prev.filter((f) => f !== format.key)
- );
+ setSelectedFormats((prev) => {
+ if (prev.length === 1) {
+ Alert.alert('Format Required', 'At least one format must be selected');
+ return prev;
+ }
+ return prev.filter((f) => f !== format.key);
+ });
}
}}
/>📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| value={selectedFormats.includes(format.key)} | |
| onValueChange={(value) => { | |
| if (value) { | |
| setSelectedFormats(prev => [...prev, format.key]); | |
| setSelectedFormats((prev) => [...prev, format.key]); | |
| } else { | |
| setSelectedFormats(prev => prev.filter(f => f !== format.key)); | |
| setSelectedFormats((prev) => | |
| prev.filter((f) => f !== format.key) | |
| ); | |
| } | |
| }} | |
| /> | |
| <Switch | |
| value={selectedFormats.includes(format.key)} | |
| onValueChange={(value) => { | |
| if (value) { | |
| setSelectedFormats((prev) => | |
| prev.includes(format.key) ? prev : [...prev, format.key] | |
| ); | |
| } else { | |
| setSelectedFormats((prev) => { | |
| if (prev.length === 1) { | |
| Alert.alert( | |
| 'Format Required', | |
| 'At least one format must be selected' | |
| ); | |
| return prev; | |
| } | |
| return prev.filter((f) => f !== format.key); | |
| }); | |
| } | |
| }} | |
| /> |
🤖 Prompt for AI Agents
In example/src/App.tsx around lines 179 to 189, the checkbox onValueChange
handler allows removing the last selected format which results in an empty
formats array and no results; modify the handler to block deselection when
selectedFormats currently has length 1 (i.e., if value is false and
selectedFormats.length === 1) and simply return early or keep the array
unchanged, otherwise proceed with the existing add/remove logic so at least one
format remains selected.
| class ImageCodeScanner: NSObject { | ||
|
|
There was a problem hiding this comment.
Module not exported — conform to RCTBridgeModule (classic bridge) or wire up TurboModule codegen
As written, the class isn’t registered with RN. For classic bridge, conform to RCTBridgeModule and expose module metadata:
-@objc(ImageCodeScanner)
-class ImageCodeScanner: NSObject {
+@objc(ImageCodeScanner)
+class ImageCodeScanner: NSObject, RCTBridgeModule {
+ static func moduleName() -> String! { "ImageCodeScanner" }
+ static func requiresMainQueueSetup() -> Bool { false }If you intend New Architecture/TurboModules, implement the codegen Spec (e.g., NativeImageCodeScannerSpec) and associated Swift adapter instead. I can provide that skeleton.
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| class ImageCodeScanner: NSObject { | |
| @objc(ImageCodeScanner) | |
| class ImageCodeScanner: NSObject, RCTBridgeModule { | |
| // Required by RCTBridgeModule to name the module exposed to JS | |
| static func moduleName() -> String! { | |
| return "ImageCodeScanner" | |
| } | |
| // If your module can be setup on a background queue, return false here. | |
| static func requiresMainQueueSetup() -> Bool { | |
| return false | |
| } | |
| // …rest of your implementation… | |
| } |
🤖 Prompt for AI Agents
In ios/ImageCodeScanner.swift around lines 9-10, the class is not registered
with React Native; either make it a classic bridge module by conforming to
RCTBridgeModule and exporting the module metadata (annotate with the ObjC
name/bridge export and implement required static metadata methods such as module
name and requiresMainQueueSetup) so RN can discover and call it, or implement
the New Architecture approach by creating the NativeImageCodeScannerSpec codegen
interface and a Swift adapter that conforms to that spec (wire up the generated
header and ensure the spec is registered in the TurboModule registry). Follow
one approach only and remove the other to avoid duplicate registrations.
| // Use atomic flag to prevent multiple promise calls | ||
| var hasResolved = false | ||
|
|
||
| func safeResolve(_ result: Any) { | ||
| if !hasResolved { | ||
| hasResolved = true | ||
| resolver(result) | ||
| } | ||
| } | ||
|
|
||
| func safeReject(_ code: String, _ message: String, _ error: Error?) { | ||
| if !hasResolved { | ||
| hasResolved = true | ||
| rejecter(code, message, error) | ||
| } | ||
| } |
There was a problem hiding this comment.
Potential race on hasResolved across threads — guard with a serial queue
safeResolve/safeReject can be called from background and main threads. Use a dedicated queue:
- var hasResolved = false
+ var hasResolved = false
+ let resolveQueue = DispatchQueue(label: "ImageCodeScanner.resolve")
- func safeResolve(_ result: Any) {
- if !hasResolved {
- hasResolved = true
- resolver(result)
- }
- }
+ func safeResolve(_ result: Any) {
+ resolveQueue.sync {
+ guard !hasResolved else { return }
+ hasResolved = true
+ resolver(result)
+ }
+ }
- func safeReject(_ code: String, _ message: String, _ error: Error?) {
- if !hasResolved {
- hasResolved = true
- rejecter(code, message, error)
- }
- }
+ func safeReject(_ code: String, _ message: String, _ error: Error?) {
+ resolveQueue.sync {
+ guard !hasResolved else { return }
+ hasResolved = true
+ rejecter(code, message, error)
+ }
+ }📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| // Use atomic flag to prevent multiple promise calls | |
| var hasResolved = false | |
| func safeResolve(_ result: Any) { | |
| if !hasResolved { | |
| hasResolved = true | |
| resolver(result) | |
| } | |
| } | |
| func safeReject(_ code: String, _ message: String, _ error: Error?) { | |
| if !hasResolved { | |
| hasResolved = true | |
| rejecter(code, message, error) | |
| } | |
| } | |
| // Use atomic flag to prevent multiple promise calls | |
| var hasResolved = false | |
| let resolveQueue = DispatchQueue(label: "ImageCodeScanner.resolve") | |
| func safeResolve(_ result: Any) { | |
| resolveQueue.sync { | |
| guard !hasResolved else { return } | |
| hasResolved = true | |
| resolver(result) | |
| } | |
| } | |
| func safeReject(_ code: String, _ message: String, _ error: Error?) { | |
| resolveQueue.sync { | |
| guard !hasResolved else { return } | |
| hasResolved = true | |
| rejecter(code, message, error) | |
| } | |
| } |
🤖 Prompt for AI Agents
In ios/ImageCodeScanner.swift around lines 85 to 100, hasResolved is accessed
from multiple threads which can race; guard access with a dedicated serial
DispatchQueue (or a lock) so safeResolve and safeReject atomically check-and-set
hasResolved before calling resolver/rejecter. Create a private serial
DispatchQueue (e.g. let resolveQueue = DispatchQueue(label:
"com.app.imagecodescanner.resolve")), then wrap the check/set and subsequent
call to resolver/rejecter inside resolveQueue.sync (or async if you need async
semantics), ensuring only one path can set hasResolved and invoke the callbacks.
| // Remove file:// prefix if present | ||
| let cleanPath = path.replacingOccurrences(of: "file://", with: "") | ||
|
|
||
| guard let originalImage = UIImage(contentsOfFile: cleanPath) else { | ||
| safeReject("INVALID_IMAGE", "Cannot load image from path: \(path)", nil) | ||
| return | ||
| } | ||
|
|
||
| // Prepare images to try - always try all preprocessing options | ||
| var imagesToTry: [(String, UIImage)] = [("Original", originalImage)] | ||
|
|
There was a problem hiding this comment.
🛠️ Refactor suggestion
Scale large images to avoid memory spikes (parity with Android)
Add a scaler (e.g., maxDimension 2048) and use it before preprocessing:
- guard let originalImage = UIImage(contentsOfFile: cleanPath) else {
+ guard let originalImage = UIImage(contentsOfFile: cleanPath) else {
safeReject("INVALID_IMAGE", "Cannot load image from path: \(path)", nil)
return
}
-
- // Prepare images to try - always try all preprocessing options
- var imagesToTry: [(String, UIImage)] = [("Original", originalImage)]
+ let baseImage = scaleImageIfNeeded(originalImage, maxDimension: 2048)
+ // Prepare images to try - always try all preprocessing options
+ var imagesToTry: [(String, UIImage)] = [("Original", baseImage)]Helper (place near preprocessing methods):
private func scaleImageIfNeeded(_ image: UIImage, maxDimension: CGFloat) -> UIImage {
let w = image.size.width, h = image.size.height
guard max(w, h) > maxDimension else { return image }
let scale = maxDimension / max(w, h)
let newSize = CGSize(width: floor(w * scale), height: floor(h * scale))
let format = UIGraphicsImageRendererFormat.default()
format.scale = 1
return UIGraphicsImageRenderer(size: newSize, format: format).image { _ in
image.draw(in: CGRect(origin: .zero, size: newSize))
}
}🤖 Prompt for AI Agents
In ios/ImageCodeScanner.swift around lines 102 to 112, large input images are
not being downscaled which can cause memory spikes; add a scaler that limits the
longest side to 2048 and apply it before any preprocessing. Implement the
provided scaleImageIfNeeded helper near the existing preprocessing methods, then
replace the originalImage usage by first calling
scaleImageIfNeeded(originalImage, maxDimension: 2048) and use that scaled image
when building imagesToTry (i.e., add ("Original", scaledImage) and pass
scaledImage into all subsequent preprocessing steps). Ensure the renderer format
uses scale = 1 to produce device-independent pixels.
| // Convert formats array to Vision symbologies | ||
| var symbologies: [VNBarcodeSymbology] = [] | ||
|
|
||
| for format in formats { | ||
| switch format { | ||
| case "QR_CODE": | ||
| symbologies.append(.qr) | ||
| case "CODE_128": | ||
| symbologies.append(.code128) | ||
| case "CODE_39": | ||
| symbologies.append(.code39) | ||
| case "CODE_93": | ||
| symbologies.append(.code93) | ||
| case "EAN_13": | ||
| symbologies.append(.ean13) | ||
| case "EAN_8": | ||
| symbologies.append(.ean8) | ||
| case "UPC_A": | ||
| symbologies.append(.upce) // Vision uses UPCE for UPC-A | ||
| case "UPC_E": | ||
| symbologies.append(.upce) | ||
| case "PDF_417": | ||
| symbologies.append(.pdf417) | ||
| case "DATA_MATRIX": | ||
| symbologies.append(.dataMatrix) | ||
| case "AZTEC": | ||
| symbologies.append(.aztec) | ||
| case "ITF": | ||
| symbologies.append(.itf14) // ITF14 format | ||
| case "CODABAR": | ||
| symbologies.append(.codabar) | ||
| default: | ||
| break | ||
| } | ||
| } |
There was a problem hiding this comment.
💡 Verification agent
❓ Verification inconclusive
UPC_A mapping: include EAN-13 for UPC-A compatibility
Vision doesn’t expose UPC-A directly; UPC-A often decodes as EAN-13 with a leading 0. When “UPC_A” is requested, include both .ean13 and .upce:
- case "UPC_A":
- symbologies.append(.upce) // Vision uses UPCE for UPC-A
+ case "UPC_A":
+ symbologies.append(contentsOf: [.ean13, .upce])Action Required: Update UPC_A Mapping to Include EAN-13 for Correct UPC-A Support
According to Apple’s AVFoundation FAQ (Technical Note TN2325), UPC-A barcodes are formally a subset of EAN-13 and are output as an EAN-13 symbology with a leading zero in the decoded string value (developer.apple.com). To ensure “UPC_A” requests are handled correctly, the code should append both .ean13 (for UPC-A) and .upce (for UPC-E) rather than only .upce.
Apply the following change in ios/ImageCodeScanner.swift (around lines 137–171):
case "UPC_A":
- symbologies.append(.upce) // Vision uses UPCE for UPC-A
+ symbologies.append(contentsOf: [.ean13, .upce]) // UPC-A via EAN-13 + UPC-E📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| // Convert formats array to Vision symbologies | |
| var symbologies: [VNBarcodeSymbology] = [] | |
| for format in formats { | |
| switch format { | |
| case "QR_CODE": | |
| symbologies.append(.qr) | |
| case "CODE_128": | |
| symbologies.append(.code128) | |
| case "CODE_39": | |
| symbologies.append(.code39) | |
| case "CODE_93": | |
| symbologies.append(.code93) | |
| case "EAN_13": | |
| symbologies.append(.ean13) | |
| case "EAN_8": | |
| symbologies.append(.ean8) | |
| case "UPC_A": | |
| symbologies.append(.upce) // Vision uses UPCE for UPC-A | |
| case "UPC_E": | |
| symbologies.append(.upce) | |
| case "PDF_417": | |
| symbologies.append(.pdf417) | |
| case "DATA_MATRIX": | |
| symbologies.append(.dataMatrix) | |
| case "AZTEC": | |
| symbologies.append(.aztec) | |
| case "ITF": | |
| symbologies.append(.itf14) // ITF14 format | |
| case "CODABAR": | |
| symbologies.append(.codabar) | |
| default: | |
| break | |
| } | |
| } | |
| case "EAN_8": | |
| symbologies.append(.ean8) | |
| case "UPC_A": | |
| symbologies.append(contentsOf: [.ean13, .upce]) // UPC-A via EAN-13 + UPC-E | |
| case "UPC_E": | |
| symbologies.append(.upce) |
🤖 Prompt for AI Agents
In ios/ImageCodeScanner.swift around lines 137 to 171, the mapping for "UPC_A"
only appends .upce but UPC-A is represented as EAN-13 (with leading zero) per
Apple TN2325; update the branch so that when format == "UPC_A" you append both
.ean13 and .upce to symbologies (keep existing UPC_E mapping as .upce), ensuring
UPC-A requests include EAN-13 support and UPC-E continues to map to UPCE.
| // Create Vision request for barcode detection | ||
| let request = VNDetectBarcodesRequest { request, error in | ||
| DispatchQueue.main.async { | ||
| if let error = error { | ||
| print("ImageCodeScanner iOS - \(description) failed: \(error.localizedDescription)") | ||
| // Try next image | ||
| tryScanning(images: images, index: index + 1) | ||
| return | ||
| } | ||
|
|
There was a problem hiding this comment.
🛠️ Refactor suggestion
Pass correct image orientation to Vision
Use the UIImage orientation when creating the handler; otherwise detection can degrade:
- let request = VNDetectBarcodesRequest { request, error in
+ let request = VNDetectBarcodesRequest { request, error in
// ...
}
// ...
- let handler = VNImageRequestHandler(cgImage: cgImage, options: [:])
+ let handler = VNImageRequestHandler(
+ cgImage: cgImage,
+ orientation: cgImagePropertyOrientation(from: currentImage.imageOrientation),
+ options: [:]
+ )Helper:
private func cgImagePropertyOrientation(from o: UIImage.Orientation) -> CGImagePropertyOrientation {
switch o {
case .up: return .up
case .down: return .down
case .left: return .left
case .right: return .right
case .upMirrored: return .upMirrored
case .downMirrored: return .downMirrored
case .leftMirrored: return .leftMirrored
case .rightMirrored: return .rightMirrored
@unknown default: return .up
}
}🤖 Prompt for AI Agents
In ios/ImageCodeScanner.swift around lines 196 to 205, the
VNDetectBarcodesRequest handler is created without passing the image orientation
to the Vision handler which can degrade detection; convert the
UIImage.Orientation to a CGImagePropertyOrientation (use a helper like the
provided cgImagePropertyOrientation(from:) function) and pass that orientation
into the VNImageRequestHandler initializer (or perform request call) so the
image is processed with correct orientation.
| "@eslint/eslintrc": "^3.3.0", | ||
| "@eslint/js": "^9.22.0", | ||
| "@evilmartians/lefthook": "^1.5.0", | ||
| "@react-native-community/cli": "15.0.0-alpha.2", | ||
| "@react-native-community/cli": "15.0.0", | ||
| "@react-native/babel-preset": "0.79.2", | ||
| "@react-native/eslint-config": "^0.78.0", | ||
| "@react-native/eslint-config": "^0.79.2", | ||
| "@release-it/conventional-changelog": "^9.0.2", | ||
| "@types/jest": "^29.5.5", | ||
| "@types/react": "^19.0.0", | ||
| "@types/react": "^18.3.12", | ||
| "commitlint": "^19.6.1", | ||
| "del-cli": "^5.1.0", | ||
| "eslint": "^9.22.0", | ||
| "eslint-config-prettier": "^10.1.1", | ||
| "eslint": "^8.56.0", | ||
| "eslint-config-prettier": "^9.1.0", | ||
| "eslint-plugin-prettier": "^5.2.3", | ||
| "jest": "^29.7.0", | ||
| "prettier": "^3.0.3", | ||
| "react": "19.0.0", | ||
| "react": "18.3.1", | ||
| "react-native": "0.79.2", | ||
| "react-native-builder-bob": "^0.40.13", | ||
| "release-it": "^17.10.0", | ||
| "turbo": "^1.10.7", | ||
| "typescript": "^5.8.3" | ||
| "typescript": "^5.3.0" | ||
| }, |
There was a problem hiding this comment.
💡 Verification agent
🧩 Analysis chain
ESLint major mismatch: @eslint/js v9 with eslint v8 will break. Align to ESLint 9.
You’re using flat config (eslint.config.mjs per PR notes). Bump eslint to v9 to match @eslint/js v9.
- "eslint": "^8.56.0",
+ "eslint": "^9.0.0",Run to confirm majors align:
🏁 Script executed:
#!/bin/bash
node -e "const p=require('./package.json'); const dj=p.devDependencies; const mj=s=>/^\\D*(\\d+)/.exec((dj[s]||'0'))?.[1]; console.log({eslint:mj('eslint'), eslint_js:mj('@eslint/js')}); if(mj('eslint')!==mj('@eslint/js')){process.exit(1)}"Length of output: 308
Bump ESLint to v9 to match @eslint/js v9
The project currently uses @eslint/js@^9.22.0 alongside eslint@^8.56.0, which will break the flat config loader. Upgrade ESLint to v9.
• File: package.json
– Location: devDependencies, lines ~82–104
- "eslint": "^8.56.0",
+ "eslint": "^9.22.0",🤖 Prompt for AI Agents
In package.json around lines 82 to 104, devDependencies list @eslint/js@^9.22.0
while eslint is pinned to ^8.56.0 which is incompatible with the v9 flat config
loader; update the eslint devDependency to a v9.x version (e.g., ^9.x) to match
@eslint/js and ensure package.json version ranges are consistent, then run
npm/yarn install and verify linting works.
| 3. **🔧 Preprocessing**: Automatic preprocessing improves recognition rates but may increase processing time. The library optimizes this automatically. | ||
|
|
||
| 4. **📱 Platform Differences**: iOS Vision Framework and Android ML Kit may have slight differences in recognition capabilities. Test on both platforms for critical use cases. |
There was a problem hiding this comment.
Troubleshooting still tells users to “enable preprocessing options,” which no longer exist.
Update bullets to reflect the automatic nature—suggest specifying formats or resizing instead.
-2. Enable preprocessing options for challenging images
-4. Try different preprocessing combinations
+2. Specify only the relevant barcode formats to reduce noise
+4. Try resizing/cropping the image (e.g., focus on the barcode area) or improving contrast before scanningCommittable suggestion skipped: line range outside the PR's diff.
🤖 Prompt for AI Agents
In README.md around lines 321 to 323, the troubleshooting text still instructs
users to “enable preprocessing options” which no longer exist; update the
bullets to state that preprocessing is automatic and cannot be toggled, remove
any references to enabling preprocessing, and instead recommend actionable steps
users can take such as providing images in supported formats, resizing or
downscaling/upscaling to recommended dimensions, and ensuring proper
color-space/rotation; also adjust any troubleshooting guidance to suggest these
format/resize checks and mention that platform differences may affect results.
| const { path, formats = [BarcodeFormat.QR_CODE] } = options; | ||
|
|
||
| // Resolve preprocessing options based on platform and overrides | ||
| const resolvedOptions = resolvePreprocessingOptions( | ||
| preprocessing, | ||
| platformOverrides, | ||
| ); | ||
|
|
||
| // Map to native module format | ||
| // Note: Preprocessing is always enabled in native implementation | ||
| // The native code automatically tries multiple preprocessing techniques | ||
| const nativeOptions = { | ||
| enhanceContrast: resolvedOptions.enhanceContrast ?? false, | ||
| convertToGrayscale: resolvedOptions.grayscale ?? false, | ||
| tryRotations: resolvedOptions.rotations ?? false, | ||
| enhanceContrast: true, | ||
| convertToGrayscale: true, | ||
| tryRotations: true, | ||
| }; | ||
|
|
||
| return ImageCodeScanner.scanFromPath( | ||
| path, | ||
| formats.map(f => f.toString()), | ||
| nativeOptions, | ||
| formats.map((f) => f.toString()), | ||
| nativeOptions | ||
| ); |
There was a problem hiding this comment.
🛠️ Refactor suggestion
Guard against empty formats and validate path early.
If consumers pass formats: [], native may see no formats. Also fail fast on missing path.
- const { path, formats = [BarcodeFormat.QR_CODE] } = options;
+ const { path, formats = [BarcodeFormat.QR_CODE] } = options;
+ if (!path) {
+ return Promise.reject(new Error('Image path is required'));
+ }
+ const effectiveFormats =
+ Array.isArray(formats) && formats.length > 0
+ ? formats
+ : [BarcodeFormat.QR_CODE];
@@
- formats.map((f) => f.toString()),
+ effectiveFormats.map((f) => String(f)),📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| const { path, formats = [BarcodeFormat.QR_CODE] } = options; | |
| // Resolve preprocessing options based on platform and overrides | |
| const resolvedOptions = resolvePreprocessingOptions( | |
| preprocessing, | |
| platformOverrides, | |
| ); | |
| // Map to native module format | |
| // Note: Preprocessing is always enabled in native implementation | |
| // The native code automatically tries multiple preprocessing techniques | |
| const nativeOptions = { | |
| enhanceContrast: resolvedOptions.enhanceContrast ?? false, | |
| convertToGrayscale: resolvedOptions.grayscale ?? false, | |
| tryRotations: resolvedOptions.rotations ?? false, | |
| enhanceContrast: true, | |
| convertToGrayscale: true, | |
| tryRotations: true, | |
| }; | |
| return ImageCodeScanner.scanFromPath( | |
| path, | |
| formats.map(f => f.toString()), | |
| nativeOptions, | |
| formats.map((f) => f.toString()), | |
| nativeOptions | |
| ); | |
| const { path, formats = [BarcodeFormat.QR_CODE] } = options; | |
| if (!path) { | |
| return Promise.reject(new Error('Image path is required')); | |
| } | |
| const effectiveFormats = | |
| Array.isArray(formats) && formats.length > 0 | |
| ? formats | |
| : [BarcodeFormat.QR_CODE]; | |
| // Note: Preprocessing is always enabled in native implementation | |
| // The native code automatically tries multiple preprocessing techniques | |
| const nativeOptions = { | |
| enhanceContrast: true, | |
| convertToGrayscale: true, | |
| tryRotations: true, | |
| }; | |
| return ImageCodeScanner.scanFromPath( | |
| path, | |
| effectiveFormats.map((f) => String(f)), | |
| nativeOptions | |
| ); |
🤖 Prompt for AI Agents
In src/index.tsx around lines 27 to 41, validate inputs before calling
ImageCodeScanner.scanFromPath: check that path is a non-empty string and throw
or return a rejected Promise immediately if missing/invalid, and ensure formats
is not an empty array (if formats === undefined use default
[BarcodeFormat.QR_CODE], and if formats is provided but formats.length === 0
replace it with the default) before mapping to strings; then pass the validated
path and formats.map(...) along with nativeOptions to
ImageCodeScanner.scanFromPath.
📝 Description
This PR introduces the first release of
react-native-image-code-scanner, a high-performance React Native library for scanning QR codes and barcodes from images with automatic preprocessing for optimal recognition.✨ Key Features
🔄 Changes Made
Core Library
Documentation & Examples
Build & CI/CD
📦 API Overview
Preprocessing is always automatic - no configuration needed!
🧪 Testing
📋 Pre-publish Checklist
🚀 Next Steps
After merging:
npm publishornpm publish --tag beta📸 Screenshots
The library includes a comprehensive example app demonstrating all features:
🔗 Related Links
🏷️ Type of Change
✅ Quality Assurance
Ready for review and merge! 🎉
Summary by CodeRabbit