Skip to content

fix: resolve CI and Android build issues with Yarn lockfile and JVM compatibility - #2

Merged
anngth merged 16 commits into
mainfrom
fix_error
Aug 28, 2025
Merged

fix: resolve CI and Android build issues with Yarn lockfile and JVM compatibility#2
anngth merged 16 commits into
mainfrom
fix_error

Conversation

@anngth

@anngth anngth commented Aug 27, 2025

Copy link
Copy Markdown
Owner

Description

This PR addresses multiple critical issues preventing successful CI builds and Android compilation:

🐛 Problems Fixed

  1. CI Workflow Failures

    • Missing yarn.lock file causing YN0028 error with --immutable flag
    • Memory allocation errors during dependency installation
    • Missing Corepack configuration for Yarn Berry
  2. Android Build Failures

    • JVM target incompatibility between Java (17) and Kotlin (11)
    • Duplicate class generation with React Native Codegen
    • Variable shadowing warnings in Kotlin code

Changes

CI/GitHub Actions

  • ✅ Added yarn.lock to version control for deterministic builds
  • ✅ Increased Node.js memory limit to 4GB (NODE_OPTIONS: --max-old-space-size=4096)
  • ✅ Enabled Corepack in setup action for proper Yarn Berry support
  • ✅ Added .yarn/cache to dependency caching strategy
  • ✅ Created reusable environment configuration (env.yml)

Android

  • ✅ Aligned Kotlin JVM target with Java 17 (was 11)
  • ✅ Fixed Codegen import path (com.imagecodescanner.NativeImageCodeScannerSpec)
  • ✅ Removed duplicate spec class that conflicted with Codegen
  • ✅ Fixed variable shadowing (optionsbitmapOptions)
  • ✅ Removed unused scanCompleted variable

Documentation & Other

  • ✅ Updated README and CHANGELOG
  • ✅ Added COMPATIBILITY.md
  • ✅ Updated ESLint configuration
  • ✅ Improvements to example app
  • ✅ Updates to iOS implementation

Testing

  • Yarn lock file is committed and tracked
  • Android builds successfully with JVM 17
  • No duplicate class errors
  • CI workflows pass without memory errors
  • Local development workflow works
  • Tested in consuming apps (Expo/React Native CLI)

Breaking Changes

None - These are internal build fixes that don't affect the API.

Type of Change

  • Bug fix (non-breaking change which fixes issues)
  • Build/CI improvements
  • Documentation update
  • Dependency/configuration update

Checklist

  • Code follows project conventions
  • Changes are tested locally
  • CI passes all checks
  • Conventional commit messages used

Summary by CodeRabbit

  • New Features

    • Automatic image preprocessing (grayscale, contrast, multi-rotation) enabled by default for better recognition.
    • Simplified scanning API: just provide image path and formats with sensible defaults.
  • Example App

    • New modern Expo cross-platform demo with image picker, format selection (requires at least one), scan timing, results view, and auto-preprocessing info.
    • Updated example build scripts to use prebuild flows.
  • Documentation

    • README and Changelog updated; new COMPATIBILITY guide (React/React Native/Expo versions, platform requirements).

@coderabbitai

coderabbitai Bot commented Aug 27, 2025

Copy link
Copy Markdown

Note

Other AI code review bot(s) detected

CodeRabbit has detected other AI code review bot(s) in this pull request and will avoid duplicating their findings in the review comments. This may lead to a less comprehensive review.

Walkthrough

This PR makes preprocessing automatic and simplifies the JS API, adds a new Swift iOS native module for scanning/preprocessing, updates Android native/build settings, overhauls docs and example (Expo) app, and adjusts CI, lint, and packaging configs.

Changes

Cohort / File(s) Summary
Docs & Messaging
CHANGELOG.md, README.md, COMPATIBILITY.md, example/README.md
Rewrote docs to emphasize automatic preprocessing, removed manual preprocessing options from public examples/API surface, added compatibility guide and updated example docs and troubleshooting.
Example App (Expo)
example/App.tsx, example/src/App.tsx, example/package.json, example/babel.config.js
Replaced re-export with full Expo app and UI; image pick, format selection (enforce ≥1), scan flow, automatic preprocessing notes; updated scripts/deps to prebuild flows and removed unused imports.
JS/TS Library API
src/index.tsx, src/NativeImageCodeScanner.ts
Simplified exported ScanOptions to { path, formats? }; removed PreprocessingOptions and platformOverrides; JS now passes fixed native preprocessing options; minor typing/formatting tweaks.
iOS Native Module
ios/ImageCodeScanner.swift, ios/ImageCodeScanner.mm, RNImageCodeScanner.podspec
Added new Swift module exposing scanFromPath with automatic grayscale/contrast/rotation/scale preprocessing using Vision; removed ObjC requiresMainQueueSetup extern; renamed pod RNImageCodeScanner.
Android Native Module & Build
android/src/main/java/.../ImageCodeScannerModule.kt, android/build.gradle
Module now extends local NativeImageCodeScannerSpec, refactored bitmap sampling names, removed scanCompleted flag; upgraded Java/Kotlin toolchain to 17 and added an extra generated-sources path.
Tooling & Lint
eslint.config.mjs, scripts/validate-package.js, package.json, react-native.config.js
ESLint export changed to array with expanded ignores and prettier rules; validation script formatting/messaging tweaks; package.json deps/peer constraints updated and commitlint rule added; added RN autolinking config.
CI/CD
.github/actions/setup/action.yml, .github/workflows/ci.yml, .github/workflows/env.yml
Enabled Corepack, adjusted caching and Node memory, added example dependency install steps, improved iOS pod install flow (Gemfile-aware), added environment workflow and CI env flags.
Repository & Packaging
package.json, RNImageCodeScanner.podspec, react-native.config.js
Updated package metadata/peers/devDeps, removed cpp from files, renamed pod, and added react-native autolinking export.

Sequence Diagram(s)

sequenceDiagram
  autonumber
  actor User
  participant App as Example App (Expo)
  participant JS as JS API
  participant Native as Native Module (iOS / Android)
  participant Engine as Vision / ML Kit

  User->>App: Pick image + select formats
  App->>JS: scan({ path, formats })
  JS->>Native: scanFromPath(path, formats, { auto-preprocess })
  rect rgba(154,208,245,0.12)
    Note right of Native: Automatic preprocessing candidates
    Native->>Native: Scale image if large
    Native->>Native: Build candidates: Original, Grayscale, Contrast, Rotations (90/180/270)
  end
  loop For each candidate until found or exhausted
    Native->>Engine: Detect barcodes (requested formats)
    Engine-->>Native: Results or none
  end
  alt Codes found
    Native-->>JS: [payloadStrings]
    JS-->>App: results
    App-->>User: show codes & timing
  else None found
    Native-->>JS: []
    JS-->>App: none
    App-->>User: inform no codes
  end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Poem

I hopped through the diff with whiskers ablur,
Spun images round—left, right, up, then recur.
Grayscale and contrast, rotations I try,
Vision and MLKit give answers—oh my!
A tidy API now, and builds running spry. 🐇📸


📜 Recent 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.

📥 Commits

Reviewing files that changed from the base of the PR and between f25f7b7 and 858885b.

📒 Files selected for processing (2)
  • .github/workflows/ci.yml (4 hunks)
  • example/package.json (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
  • example/package.json
  • .github/workflows/ci.yml
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
  • GitHub Check: build-ios
  • GitHub Check: build-android
✨ Finishing Touches
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch fix_error

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.

❤️ Share
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbit in a new review comment at the desired location with your query.
  • PR comments: Tag @coderabbit in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbit gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbit read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.

Support

Need help? Create a ticket on our support page for assistance with any issues or questions.

CodeRabbit Commands (Invoked using PR/Issue comments)

Type @coderabbit help to get the list of available commands.

Other keywords and placeholders

  • Add @coderabbit ignore or @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbit summary or @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbit or @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Status, Documentation and Community

  • Visit our Status Page to check the current availability of CodeRabbit.
  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

8 issues found across 16 files

React with 👍 or 👎 to teach cubic. You can also tag @cubic-dev-ai to give feedback, ask questions, or re-run the review.

Comment thread CHANGELOG.md Outdated
Comment thread example/App.tsx Outdated
Comment thread ios/ImageCodeScanner.swift Outdated
Comment thread ios/ImageCodeScanner.swift Outdated
symbologies.append(.ean8)
case "UPC_A":
symbologies.append(contentsOf: [.ean13, .upce]) // UPC-A via EAN-13 + UPC-E
case "UPC_E":

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Limit UPC_A mapping to .ean13; including .upce broadens results beyond the requested format.

Prompt for AI agents
Address the following comment on ios/ImageCodeScanner.swift at line 189:

<comment>Limit UPC_A mapping to .ean13; including .upce broadens results beyond the requested format.</comment>

<file context>
@@ -1 +1,323 @@
+        symbologies.append(.ean8)
+      case &quot;UPC_A&quot;:
+        symbologies.append(contentsOf: [.ean13, .upce])  // UPC-A via EAN-13 + UPC-E
+      case &quot;UPC_E&quot;:
+        symbologies.append(.upce)
+      case &quot;PDF_417&quot;:
</file context>


// Always add rotated versions
if let rotated90 = rotateImage(originalImage, degrees: 90) {
imagesToTry.append(("Rotated 90°", rotated90))

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Rotate the scaled baseImage instead of the original to reduce memory/CPU usage.

Prompt for AI agents
Address the following comment on ios/ImageCodeScanner.swift at line 160:

<comment>Rotate the scaled baseImage instead of the original to reduce memory/CPU usage.</comment>

<file context>
@@ -1 +1,323 @@
+    
+    // Always add rotated versions
+    if let rotated90 = rotateImage(originalImage, degrees: 90) {
+      imagesToTry.append((&quot;Rotated 90°&quot;, rotated90))
+    }
+    if let rotated180 = rotateImage(originalImage, degrees: 180) {
</file context>

Comment thread ios/ImageCodeScanner.swift
Comment thread eslint.config.mjs Outdated

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 4

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (4)
scripts/validate-package.js (1)

10-32: Enforce lockfile presence in requiredFiles (aligns with PR goal).
Add yarn.lock so validation guards deterministic CI installs.

   'CHANGELOG.md',
+  'yarn.lock',
   'tsconfig.json',
CHANGELOG.md (1)

50-50: Broken release tag v0.1.0 – please create or correct it before merging

The changelog entry in CHANGELOG.md points to a non-existent GitHub release tag (HTTP 404). You must either publish a release/tag v0.1.0 in the repository or update this link to reference the correct existing version.

• File: CHANGELOG.md
Line 50:

[0.1.0]: https://github.com/nguyenthanhan/react-native-image-code-scanner/releases/tag/v0.1.0

• Action: Create the v0.1.0 tag under the GitHub “Releases” tab or adjust the URL to the accurate tag name.

README.md (2)

381-386: Docs inconsistency: “Real-time preprocessing option toggles” vs always-on preprocessing.

If the library doesn’t expose toggles, clarify that the example shows informational UI only.

Apply:

-- Real-time preprocessing option toggles
+- Automatic preprocessing (always on; no manual toggles required)

85-91: Add Photo Library usage string for gallery access.

Most pickers require NSPhotoLibraryUsageDescription (and optionally NSPhotoLibraryAddUsageDescription if saving).

Apply:

 <key>NSCameraUsageDescription</key>
 <string>This app needs access to camera to scan barcodes</string>
+<key>NSPhotoLibraryUsageDescription</key>
+<string>This app needs access to your photos to pick images for scanning</string>
🧹 Nitpick comments (20)
scripts/validate-package.js (5)

38-45: Gate “Found” logs behind a verbose flag to reduce CI noise.
Keeps summaries clean while still allowing detail when needed.

-  } else {
-    console.log(`✅ Found: ${file}`);
-  }
+  } else {
+    if (process.env.VALIDATE_VERBOSE) console.log(`✅ Found: ${file}`);
+  }

53-79: Harden package.json checks for modern packaging and determinism.
Allow exports, typings; add license and packageManager checks; make the “validated” message conditional.

-  if (!packageJson.name) {
+  if (!packageJson.name) {
     errors.push('❌ package.json: missing "name" field');
   }
   if (!packageJson.version) {
     errors.push('❌ package.json: missing "version" field');
   }
-  if (!packageJson.main) {
-    errors.push('❌ package.json: missing "main" field');
-  }
-  if (!packageJson.types) {
-    errors.push('❌ package.json: missing "types" field');
-  }
+  if (!packageJson.main && !packageJson.exports) {
+    errors.push('❌ package.json: missing "main" or "exports" field');
+  }
+  if (!packageJson.types && !packageJson.typings) {
+    errors.push('❌ package.json: missing "types"/"typings" field');
+  }
   if (!packageJson.repository) {
     warnings.push('⚠️  package.json: missing "repository" field');
   }
   if (!packageJson.keywords || packageJson.keywords.length === 0) {
     warnings.push('⚠️  package.json: missing or empty "keywords" field');
   }
   if (!packageJson.peerDependencies) {
     errors.push('❌ package.json: missing "peerDependencies" field');
   }
+  if (!packageJson.license) {
+    warnings.push('⚠️  package.json: missing "license" field');
+  }
+  if (!packageJson.packageManager) {
+    warnings.push('⚠️  package.json: missing "packageManager" (e.g., "yarn@<version>")');
+  }
- 
-  console.log('✅ package.json structure validated');
+ 
+  // Only claim validation success if no new issues were added by this block.
+  console.log(
+    errors.length === 0 && warnings.length === 0
+      ? '✅ package.json structure validated'
+      : 'ℹ️  package.json checked; see issues recorded above'
+  );

86-91: Flag common build artifacts as unwanted.
Add dist (and keep as a warning) to avoid accidental publishes.

-const unwantedDirs = ['modules', 'build', 'lib', '.turbo'];
+const unwantedDirs = ['modules', 'build', 'lib', 'dist', '.turbo'];

103-103: Use console.error for error lines.
Better severity signaling in CI logs.

-    errors.forEach((error) => console.log('  ' + error));
+    errors.forEach((error) => console.error('  ' + error));

108-108: Use console.warn for warnings.
Improves log clarity.

-    warnings.forEach((warning) => console.log('  ' + warning));
+    warnings.forEach((warning) => console.warn('  ' + warning));
example/README.md (1)

97-99: Align “automatic preprocessing” messaging across the README.

These bullets say preprocessing is automatic, but elsewhere the README still mentions toggling preprocessing (Features, Usage step 3, Troubleshooting). Unify to avoid confusing users.

Apply these doc tweaks (outside this hunk) for consistency:

- - ⚙️ **Preprocessing Options**: Image enhancement for better recognition
- - 🎯 **Real-time Configuration**: Toggle preprocessing options on the fly
+ - ⚙️ **Automatic Preprocessing**: Built-in image enhancement (contrast, grayscale, auto-rotations)
+ - 🎯 **Real-time Configuration**: Choose barcode formats; preprocessing runs automatically
-3. **Configure Preprocessing**: Enable/disable image enhancement options
+3. **Preprocessing**: Runs automatically; no configuration needed
-**No Barcodes Detected:**
-- Try enabling preprocessing options
+**No Barcodes Detected:**
+- Preprocessing is automatic; verify image quality and orientation
src/NativeImageCodeScanner.ts (1)

5-13: Optional refactor – loosen the native options param for future flexibility

The JS wrapper already hides manual preprocessing (always supplies its own nativeOptions), so there’s no contract drift for existing callers. However, relaxing the spec signature now will:

  • Decouple the internal TurboModule API from manual-preprocessing concerns
  • Allow any future callers (or generated bindings) to omit options entirely
  • Align better with RN codegen conventions around optional parameters

Proposed change in src/NativeImageCodeScanner.ts:

 export interface Spec extends TurboModule {
   scanFromPath(
     path: string,
     formats: string[],
-    options: {
-      enhanceContrast: boolean;
-      convertToGrayscale: boolean;
-      tryRotations: boolean;
-    }
+    options?: {
+      enhanceContrast?: boolean;
+      convertToGrayscale?: boolean;
+      tryRotations?: boolean;
+    } | null
   ): Promise<string[]>;
 }

No updates to src/index.tsx are required—the wrapper still passes its default nativeOptions.

eslint.config.mjs (2)

18-19: Reconsider ignoring example/ if you want it linted.**

Ignoring example/** skips lint on the demo app. If you intended to lint it, drop it from ignores or add a dedicated block with different rules.


27-33: Prettier options OK; consider pinning via eslint-config-prettier and a .prettierrc for editor parity.

To avoid drift across tools, mirror these options in a .prettierrc and keep eslint-config-prettier in dev deps.

CHANGELOG.md (1)

21-27: “Optional disable switch” claim may not match current iOS implementation.

Swift code unconditionally applies preprocessing and ignores the options parameter. Either add an option to disable preprocessing in native code or adjust wording to “always-on preprocessing.”

ios/ImageCodeScanner.swift (2)

112-112: Prefer RCTLog over print for RN-friendly logging.

Switch print calls to RCTLogInfo (import React/RCTLog.h) or guard with a debug flag.


292-322: Dead code: fallback never invoked.

Either call tryQRCodeFallback after all Vision attempts (for QR-only) or remove it.

COMPATIBILITY.md (1)

56-64: Android SDK levels may be outdated soon.

Consider targeting/compiling with the latest SDK (e.g., 34+) to satisfy Play requirements; document min/target separately.

README.md (2)

248-255: TypeScript enum caveat.

If BarcodeFormat is a string enum, cast is fine. If numeric, Object.values returns mixed types. Ensure it’s string-valued or filter.

Optional tweak:

-      formats: Object.values(BarcodeFormat), // All supported formats
+      formats: Object.values(BarcodeFormat) as BarcodeFormat[], // All supported formats

97-101: Android SDK versions: consider updating compileSdk.

Docs say compileSdkVersion 33+. Align with current Android SDK level used in the project to avoid build warnings.

example/App.tsx (3)

98-101: Normalize file:// URIs before scanning (Android safety).

Some Android paths from ImagePicker include file://; native code often expects a raw path. Normalize before calling scan.

Apply this diff:

-      const results = await ImageCodeScanner.scan({
-        path: selectedImage,
+      const scanPath =
+        Platform.OS === 'android' && selectedImage.startsWith('file://')
+          ? selectedImage.slice(7)
+          : selectedImage;
+      const results = await ImageCodeScanner.scan({
+        path: scanPath,
         formats: selectedFormats,
       });

25-32: Remove unused enabled flags in BARCODE_FORMATS.

The enabled property isn’t read; it can mislead future readers.

Apply this diff:

-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' },
+];

34-284: Consolidate App implementations into a single source of truth

We’ve confirmed there are two distinct App.tsx files in the example project, but only the root one is used as Expo’s entrypoint (example/package.json"main": "App.tsx"). Maintaining both risks UI/style drift over time.

Key locations:

  • example/package.json (defines "main": "App.tsx")
  • example/App.tsx (active entrypoint)
  • example/src/App.tsx (unused duplicate)

Recommended refactors:

  • Remove or archive example/src/App.tsx, and consolidate any needed code into the root App.tsx
  • Or have the root App.tsx re-export from src/App.tsx and update package.json if you prefer the src structure
  • Ensure styles, fonts, and UI logic reside in a single file to prevent divergence
example/src/App.tsx (1)

105-109: Normalize file:// URIs before scanning (Android safety).

Same concern as the other App: strip file:// when present.

Apply this diff:

-      const results = await ImageCodeScanner.scan({
-        path: selectedImage,
+      const scanPath =
+        Platform.OS === 'android' && selectedImage.startsWith('file://')
+          ? selectedImage.slice(7)
+          : selectedImage;
+      const results = await ImageCodeScanner.scan({
+        path: scanPath,
         formats: selectedFormats,
         // Preprocessing is always enabled automatically for optimal results
       });
src/index.tsx (1)

27-42: Harden formats handling and avoid redundant toString; add path normalization.

  • Filter formats against known values; fallback if empty.
  • Pass formats directly (already strings).
  • Normalize file:// paths to raw paths before native call.

Apply this diff:

-    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];
+    const allowed = new Set<string>(Object.values(BarcodeFormat));
+    const normalizedFormats =
+      Array.isArray(formats) && formats.length > 0
+        ? formats.filter((f) => allowed.has(f))
+        : [BarcodeFormat.QR_CODE];
+    const effectiveFormats =
+      normalizedFormats.length > 0 ? normalizedFormats : [BarcodeFormat.QR_CODE];
 
     // Note: Preprocessing is always enabled in native implementation
     // The native code automatically tries multiple preprocessing techniques
-    const nativeOptions = {
+    const nativeOptions = {
       enhanceContrast: true,
       convertToGrayscale: true,
       tryRotations: true,
     };
 
     return ImageCodeScanner.scanFromPath(
-      path,
-      effectiveFormats.map((f) => f.toString()),
+      path.startsWith('file://') ? path.slice(7) : path,
+      effectiveFormats,
       nativeOptions
     );

Also applies to: 44-48

📜 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.

📥 Commits

Reviewing files that changed from the base of the PR and between eb5f74d and 05496d1.

⛔ Files ignored due to path filters (1)
  • yarn.lock is excluded by !**/yarn.lock, !**/*.lock
📒 Files selected for processing (15)
  • CHANGELOG.md (2 hunks)
  • COMPATIBILITY.md (1 hunks)
  • README.md (14 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.mm (0 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)
💤 Files with no reviewable changes (1)
  • ios/ImageCodeScanner.mm
🧰 Additional context used
🧬 Code graph analysis (3)
example/App.tsx (1)
example/src/App.tsx (1)
  • App (36-257)
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)
scripts/validate-package.js (1)
eslint.config.mjs (1)
  • __dirname (9-9)
🪛 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)

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)

README.md

[grammar] ~35-~35: There might be a mistake here.
Context: ...nality) ## 📋 Supported Barcode Formats | Format | iOS | Android | | ------...

(QB_NEW_EN)


[grammar] ~37-~37: There might be a mistake here.
Context: ...Formats | Format | iOS | Android | | ----------- | --- | ------- | | QR Cod...

(QB_NEW_EN)


[grammar] ~38-~38: There might be a mistake here.
Context: ...ndroid | | ----------- | --- | ------- | | QR Code | ✅ | ✅ | | Code 128...

(QB_NEW_EN)


[grammar] ~39-~39: There might be a mistake here.
Context: ... ------- | | QR Code | ✅ | ✅ | | Code 128 | ✅ | ✅ | | Code 39 ...

(QB_NEW_EN)


[grammar] ~40-~40: There might be a mistake here.
Context: ...| ✅ | | Code 128 | ✅ | ✅ | | Code 39 | ✅ | ✅ | | Code 93 ...

(QB_NEW_EN)


[grammar] ~41-~41: There might be a mistake here.
Context: ...| ✅ | | Code 39 | ✅ | ✅ | | Code 93 | ✅ | ✅ | | EAN-13 ...

(QB_NEW_EN)


[grammar] ~42-~42: There might be a mistake here.
Context: ...| ✅ | | Code 93 | ✅ | ✅ | | EAN-13 | ✅ | ✅ | | EAN-8 ...

(QB_NEW_EN)


[grammar] ~43-~43: There might be a mistake here.
Context: ...| ✅ | | EAN-13 | ✅ | ✅ | | EAN-8 | ✅ | ✅ | | UPC-A ...

(QB_NEW_EN)


[grammar] ~44-~44: There might be a mistake here.
Context: ...| ✅ | | EAN-8 | ✅ | ✅ | | UPC-A | ✅ | ✅ | | UPC-E ...

(QB_NEW_EN)


[grammar] ~45-~45: There might be a mistake here.
Context: ...| ✅ | | UPC-A | ✅ | ✅ | | UPC-E | ✅ | ✅ | | PDF417 ...

(QB_NEW_EN)


[grammar] ~46-~46: There might be a mistake here.
Context: ...| ✅ | | UPC-E | ✅ | ✅ | | PDF417 | ✅ | ✅ | | Data Mat...

(QB_NEW_EN)


[grammar] ~47-~47: There might be a mistake here.
Context: ...| ✅ | | PDF417 | ✅ | ✅ | | Data Matrix | ✅ | ✅ | | Aztec ...

(QB_NEW_EN)


[grammar] ~48-~48: There might be a mistake here.
Context: ...| ✅ | | Data Matrix | ✅ | ✅ | | Aztec | ✅ | ✅ | | ITF ...

(QB_NEW_EN)


[grammar] ~49-~49: There might be a mistake here.
Context: ...| ✅ | | Aztec | ✅ | ✅ | | ITF | ✅ | ✅ | | Codabar ...

(QB_NEW_EN)


[grammar] ~50-~50: There might be a mistake here.
Context: ...| ✅ | | ITF | ✅ | ✅ | | Codabar | ✅ | ✅ | ## Compat...

(QB_NEW_EN)


[grammar] ~55-~55: There might be a mistake here.
Context: ... | | -------------------- | ---------------...

(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: ...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] ~63-~63: There might be a mistake here.
Context: ...quirements - React Native: >=0.70.0 - React: >=17.0.0 - iOS: 13.4+ - **A...

(QB_NEW_EN)


[grammar] ~64-~64: There might be a mistake here.
Context: ...Native**: >=0.70.0 - React: >=17.0.0 - iOS: 13.4+ - Android: minSdkVersio...

(QB_NEW_EN)


[grammar] ~65-~65: There might be a mistake here.
Context: ...0 - React: >=17.0.0 - iOS: 13.4+ - Android: minSdkVersion 21+ - Node:...

(QB_NEW_EN)


[grammar] ~66-~66: There might be a mistake here.
Context: ...: 13.4+ - Android: minSdkVersion 21+ - Node: >=18 ## Installation ```bash n...

(QB_NEW_EN)


[grammar] ~177-~177: There might be a mistake here.
Context: .... Original Image: First scan attempt 2. Grayscale Conversion: Improves detecti...

(QB_NEW_EN)


[grammar] ~178-~178: There might be a mistake here.
Context: ...mproves detection in colored backgrounds 3. Contrast Enhancement: Better recogniti...

(QB_NEW_EN)


[grammar] ~179-~179: There might be a mistake here.
Context: ...etter recognition in low-contrast images 4. Rotation Attempts: Tries 0°, 90°, 180°...

(QB_NEW_EN)


[grammar] ~274-~274: There might be a mistake here.
Context: ... | Required | Description | | --------- | ------------- | -------- |...

(QB_NEW_EN)


[grammar] ~275-~275: There might be a mistake here.
Context: ...-- | -------- | ---------------------- | | options | ScanOptions | Yes |...

(QB_NEW_EN)


[grammar] ~320-~320: There might be a mistake here.
Context: ... automatically - Smart Retry Logic: If initial scan fails, automatically tries...

(QB_NEW_EN)


[grammar] ~390-~390: There might be a mistake here.
Context: ...testing, but barcode scanning won't work - ⚡ Prebuild Mode: Full functionality ...

(QB_NEW_EN)


[grammar] ~391-~391: There might be a mistake here.
Context: ...scanning (requires Xcode/Android Studio) - 📖 **See [QUICK_START.md](./example/QUIC...

(QB_NEW_EN)


[grammar] ~392-~392: There might be a mistake here.
Context: ...RT.md)** for detailed setup instructions Platform Support: - 📱 iOS: Full ...

(QB_NEW_EN)


[grammar] ~396-~396: There might be a mistake here.
Context: ... iOS: Full camera and gallery access - 🤖 Android: Full camera and gallery ...

(QB_NEW_EN)


[grammar] ~397-~397: There might be a mistake here.
Context: ...ndroid**: Full camera and gallery access - 🌐 Web: Photo library access (camera...

(QB_NEW_EN)

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: cubic · AI code reviewer
🔇 Additional comments (16)
scripts/validate-package.js (2)

6-8: Banner log LGTM.
Clear start message; no functional change.


49-52: JSON parsing block LGTM.
Readable and correctly scoped.

example/package.json (1)

20-21: Expo dependency bump verified

  • example/package.json shows:
    • expo @~52.0.0
    • expo-image-picker @~16.0.3
    • expo-status-bar @~2.0.1
  • .yarnrc.yml exists and has nodeLinker: node-modules, so Yarn PnP won’t interfere with React Native tooling.

Everything looks compatible with Expo 52.0.0. You can proceed, and as recommended, run expo doctor and expo prebuild --clean within the example project to catch any remaining integration issues early.

example/babel.config.js (1)

1-6: LGTM — minimal and correct Babel config for Expo.

package.json (3)

4-4: Description update matches the new “automatic preprocessing” positioning.


85-104: Dev toolchain upgrades: verify full build and test environment on Node 18

• React Native and CLI versions are correctly pegged to 0.79.2 and 15.0.0, respectively
react-native-builder-bob (^0.40.13) and your codegenConfig block are present in package.json
• Sandbox shows Node v24.3.0—please switch to Node 18.x (the RN 0.79 LTS recommendation) and ensure:
– A clean library build:
bash npx react-native-builder-bob build
– All tests (Jest preset for RN 0.79) pass:
bash npm test
• Finally, confirm that your codegen outputs are generated correctly under Bob’s build


106-108: No docs update needed: minimum versions already documented

The minimum peer dependency versions (React >=17.0.0 and React Native >=0.70.0) are already reflected in the docs:

  • README.md (lines 63–64):
    “React Native: >=0.70.0” and “React: >=17.0.0”
  • COMPATIBILITY.md (lines 119–120):
    "react": ">=17.0.0", and "react-native": ">=0.70.0"

No additional changes required.

src/NativeImageCodeScanner.ts (1)

1-2: Import style change is fine.

ios/ImageCodeScanner.swift (1)

8-13: Obj-C Shim Verified – No Further Action Required
I’ve confirmed that ios/ImageCodeScanner.mm (line 4) contains the required
RCT_EXTERN_MODULE(ImageCodeScanner, NSObject) shim, and your Swift class in
ios/ImageCodeScanner.swift implements both moduleName() and
requiresMainQueueSetup(). The native module is correctly exposed to React Native.

COMPATIBILITY.md (2)

18-20: Forward-looking compatibility entries require tentative marking

Please update the COMPATIBILITY.md table (lines 18–20) to treat future compatibility as provisional:

  • For React Native 0.80.x+
    • Change the Expo SDK column from “🔜 0.1.x” to “🔜 SDK 53” (the latest stable Expo SDK is 53) (expo.dev)
    • Change the support column from “Ready when released” to “Tentative: confirm at SDK 53 stable release”

  • For React Native 0.79.x / Expo SDK 52
    • Clarify that SDK 52 is the current latest stable SDK fully supported by React Native 0.79.x (reactnative.dev)

Once Expo SDK 53 and React Native 0.80.x+ are officially released and validated, update these entries with the final compatibility status.


65-70: All version requirements are in sync

  • package.json .engines.node >= 18 matches COMPATIBILITY.md’s Node.js >= 18.0.0 requirement.
  • package.json .peerDependencies.react >= 17.0.0 and .peerDependencies.react-native >= 0.70.0 align with the React >= 17 and RN >= 0.70 entries in the docs.
  • GitHub Actions CI (publish.yml) installs Node 18 via actions/setup-node@v4 (node-version: '18'), satisfying the docs’ Node requirement.

No further updates needed.

example/App.tsx (1)

327-333: Verify RN min version for gap style.

gap in Flexbox requires newer RN. Confirm COMPATIBILITY.md declares a supported RN version where gap works across platforms.

example/src/App.tsx (2)

127-142: Nice guard: enforce at least one format.

Prevents empty format arrays and avoids confusing scans.


303-307: Verify RN min version for gap style.

gap requires recent RN; ensure compatibility matrix reflects this.

src/index.tsx (2)

36-43: Confirm native option keys match both platforms.

Ensure enhanceContrast, convertToGrayscale, and tryRotations align with the native module signatures on iOS and Android.


20-23: API simplification looks good.

Reducing ScanOptions to path and formats matches the example apps and docs.

Comment thread eslint.config.mjs
Comment thread example/App.tsx
Comment thread ios/ImageCodeScanner.swift
Comment thread ios/ImageCodeScanner.swift

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
.github/workflows/ci.yml (1)

149-156: Broken condition references undefined steps.cocoapods-cache

This will evaluate against a non-existent step and can break the job. Either add the cache step with id cocoapods-cache or remove that part of the condition.

Option A — add cache step (recommended):

   build-ios:
     runs-on: macos-latest
@@
       - name: Cache turborepo for iOS
         uses: actions/cache@5a3ec84eff668545956fd18022155c47e93e2684 # v4.2.3
@@
+      - name: Cache cocoapods
+        id: cocoapods-cache
+        uses: actions/cache@5a3ec84eff668545956fd18022155c47e93e2684 # v4.2.3
+        with:
+          path: |
+            example/ios/Pods
+            example/vendor/bundle
+          key: ${{ runner.os }}-cocoapods-${{ hashFiles('example/ios/Podfile.lock') }}
+          restore-keys: |
+            ${{ runner.os }}-cocoapods-
@@
-      - name: Install cocoapods
-        if: env.turbo_cache_hit != 1 && steps.cocoapods-cache.outputs.cache-hit != 'true'
+      - name: Install cocoapods
+        if: env.turbo_cache_hit != 1 && steps.cocoapods-cache.outputs.cache-hit != 'true'
         run: |
           cd example
           bundle install
           bundle exec pod repo update --verbose
           bundle exec pod install --project-directory=ios

Option B — simplify condition:

-      - name: Install cocoapods
-        if: env.turbo_cache_hit != 1 && steps.cocoapods-cache.outputs.cache-hit != 'true'
+      - name: Install cocoapods
+        if: env.turbo_cache_hit != 1
🧹 Nitpick comments (9)
.github/actions/setup/action.yml (4)

12-15: Add corepack install to honor repo’s Yarn version early

Ensures the Yarn version from packageManager is prepared before caching/installs; fails fast if misconfigured.

     - name: Enable Corepack
       run: corepack enable
       shell: bash
+
+    - name: Prepare Yarn from packageManager
+      run: corepack install
+      shell: bash

20-27: Broaden cache key inputs to avoid stale caches

Include Yarn config and plugins; these affect resolution.

       with:
         path: |
           **/node_modules
           .yarn/cache
           .yarn/install-state.gz
-        key: ${{ runner.os }}-yarn-${{ hashFiles('yarn.lock') }}-${{ hashFiles('**/package.json', '!node_modules/**') }}
+        key: ${{ runner.os }}-yarn-${{ hashFiles('yarn.lock') }}-${{ hashFiles('**/package.json', '.yarnrc.yml', '.yarn/plugins/**', '!node_modules/**') }}
         restore-keys: |
           ${{ runner.os }}-yarn-${{ hashFiles('yarn.lock') }}
           ${{ runner.os }}-yarn-

29-35: Ensure project cache is used (aligns with caching .yarn/cache)

Without disabling the global cache here, Yarn may ignore .yarn/cache.

     - name: Install dependencies
       if: steps.yarn-cache.outputs.cache-hit != 'true'
       run: yarn install --immutable
       shell: bash
       env:
         NODE_OPTIONS: --max-old-space-size=4096
+        YARN_ENABLE_GLOBAL_CACHE: "false"

36-44: Mirror cache key improvements for save step

Keep save/restore keys consistent.

       with:
         path: |
           **/node_modules
           .yarn/cache
           .yarn/install-state.gz
-        key: ${{ steps.yarn-cache.outputs.cache-primary-key }}
+        key: ${{ steps.yarn-cache.outputs.cache-primary-key }}

Note: No change to the key expression; ensure the restore and save key inputs remain in sync after the earlier key update.

.github/workflows/ci.yml (2)

13-18: Add YARN_ENABLE_GLOBAL_CACHE: false for deterministic, repo-local caching

Matches env workflow and the composite action’s .yarn/cache strategy.

 env:
   # Increase Node.js memory to prevent allocation errors
   NODE_OPTIONS: --max-old-space-size=4096
   # Ensure Yarn uses immutable installs
   YARN_ENABLE_IMMUTABLE_INSTALLS: true
+  # Use project cache to match CI caching
+  YARN_ENABLE_GLOBAL_CACHE: false

70-85: Harden turbo cache detection step

Fail fast on command errors and avoid unbound var pitfalls.

       - name: Check turborepo cache for Android
-        run: |
+        run: |
+          set -euo pipefail
           TURBO_CACHE_STATUS=$(node -p "($(yarn turbo run build:android --cache-dir=\"${{ env.TURBO_CACHE_DIR }}\" --dry=json)).tasks.find(t => t.task === 'build:android').cache.status")
 
           if [[ $TURBO_CACHE_STATUS == "HIT" ]]; then
             echo "turbo_cache_hit=1" >> $GITHUB_ENV
           fi

Apply the same hardening to the iOS check (Lines 135-141).

.github/workflows/env.yml (3)

6-17: Trailing spaces detected; fix to satisfy linters

YAMLlint flags trailing whitespace on Lines 9, 13, 17.

   NODE_OPTIONS: --max-old-space-size=4096
-  
+
   # Yarn settings for better CI performance
   YARN_ENABLE_IMMUTABLE_INSTALLS: true
   YARN_ENABLE_GLOBAL_CACHE: false
-  
+
   # Disable telemetry for various tools
   NEXT_TELEMETRY_DISABLED: 1
   TURBO_TELEMETRY_DISABLED: 1
-  
+

22-29: More trailing spaces (YAMLlint) and minor enhancement

Clean Lines 24 and 27; optionally output memory and versions in a single step to reduce overhead.

       - name: Check Node version
         run: node --version
-      
+
       - name: Check Yarn version
         run: yarn --version
-      
+
       - name: Check available memory
         run: free -h

Optional consolidation:

+      - name: Environment summary
+        run: |
+          echo "Node: $(node --version)"
+          echo "Yarn: $(yarn --version)"
+          echo "Memory:"
+          free -h

1-17: Make this reusable workflow effective by invoking it

Currently it’s defined but not used. If you intend to run it before CI, add a job in CI that “uses” this workflow.

Example in .github/workflows/ci.yml:

 jobs:
+  preflight:
+    uses: ./.github/workflows/env.yml
+
   lint:
📜 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.

📥 Commits

Reviewing files that changed from the base of the PR and between 05496d1 and 2bd12b3.

⛔ Files ignored due to path filters (1)
  • yarn.lock is excluded by !**/yarn.lock, !**/*.lock
📒 Files selected for processing (3)
  • .github/actions/setup/action.yml (2 hunks)
  • .github/workflows/ci.yml (1 hunks)
  • .github/workflows/env.yml (1 hunks)
🧰 Additional context used
🪛 YAMLlint (1.37.1)
.github/workflows/env.yml

[error] 9-9: trailing spaces

(trailing-spaces)


[error] 13-13: trailing spaces

(trailing-spaces)


[error] 17-17: trailing spaces

(trailing-spaces)


[error] 24-24: trailing spaces

(trailing-spaces)


[error] 27-27: trailing spaces

(trailing-spaces)

🔇 Additional comments (1)
.github/workflows/ci.yml (1)

115-123: Verify Xcode 16.2 availability on macos-latest image

Pinned versions can disappear from the runner image’s catalog; if unavailable, the job fails. Consider making this configurable or confirm availability.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

♻️ Duplicate comments (1)
example/App.tsx (1)

385-408: Remove unused style rules (optionRow, optionLabel, warningText)

They’re not referenced; trim to reduce noise.

   warningText: {
-    fontSize: 13,
-    color: '#ff6b6b',
-    fontStyle: 'italic',
-    marginTop: 8,
+    // removed unused style
   },

Alternatively, delete optionRow, optionLabel, and warningText blocks entirely.

🧹 Nitpick comments (9)
example/App.tsx (4)

42-85: Consolidate pickers into a single helper to remove duplication and centralize permission UX

Both image-picking functions duplicate logic. Combine them to cut maintenance and keep messages consistent.

-  const pickImageFromCamera = async () => {
-    const { status } = await ImagePicker.requestCameraPermissionsAsync();
-    if (status !== 'granted') {
-      Alert.alert(
-        'Permission Denied',
-        'Camera permission is required to take photos'
-      );
-      return;
-    }
-
-    const result = await ImagePicker.launchCameraAsync({
-      mediaTypes: ImagePicker.MediaTypeOptions.Images,
-      allowsEditing: false,
-      quality: 1,
-    });
-
-    if (!result.canceled && result.assets[0]) {
-      setSelectedImage(result.assets[0].uri);
-      setScanResult(null);
-    }
-  };
-
-  const pickImageFromGallery = async () => {
-    const { status } = await ImagePicker.requestMediaLibraryPermissionsAsync();
-    if (status !== 'granted') {
-      Alert.alert(
-        'Permission Denied',
-        'Gallery permission is required to select photos'
-      );
-      return;
-    }
-
-    const result = await ImagePicker.launchImageLibraryAsync({
-      mediaTypes: ImagePicker.MediaTypeOptions.Images,
-      allowsEditing: false,
-      quality: 1,
-    });
-
-    if (!result.canceled && result.assets[0]) {
-      setSelectedImage(result.assets[0].uri);
-      setScanResult(null);
-    }
-  };
+  const handleImagePicker = async (type: 'camera' | 'gallery') => {
+    const perm =
+      type === 'camera'
+        ? await ImagePicker.requestCameraPermissionsAsync()
+        : await ImagePicker.requestMediaLibraryPermissionsAsync();
+    if (perm.status !== 'granted') {
+      Alert.alert(
+        'Permission Denied',
+        `${type === 'camera' ? 'Camera' : 'Gallery'} permission is required`
+      );
+      return;
+    }
+    const result =
+      type === 'camera'
+        ? await ImagePicker.launchCameraAsync({
+            mediaTypes: ImagePicker.MediaTypeOptions.Images,
+            allowsEditing: false,
+            quality: 1,
+          })
+        : await ImagePicker.launchImageLibraryAsync({
+            mediaTypes: ImagePicker.MediaTypeOptions.Images,
+            allowsEditing: false,
+            quality: 1,
+          });
+    if (!result.canceled && result.assets[0]) {
+      setSelectedImage(result.assets[0].uri);
+      setScanResult(null);
+    }
+  };

157-168: Wire buttons to the unified picker helper

-              onPress={pickImageFromCamera}
+              onPress={() => handleImagePicker('camera')}
...
-              onPress={pickImageFromGallery}
+              onPress={() => handleImagePicker('gallery')}

111-116: Avoid duplicate UX: drop the alert; the UI already shows a no-results panel

Prevents double messaging and keeps the flow quieter.

-      if (results.length === 0) {
-        Alert.alert(
-          'No Codes Found',
-          'No barcodes were detected in the image. Try using a clearer image or different barcode formats.'
-        );
-      }

327-364: The 'gap' style requires newer RN; add a fallback or adjust to margins to match stated RN >=0.70 support

RN’s flex gap landed relatively recently. Either keep the example’s minimum RN aligned with gap support or replace with margins between children.

CHANGELOG.md (1)

46-47: Fix inconsistency: example app doesn’t expose a disable switch

Change “optional disable switch” to reflect the current implementation (info only).

-- Automatic preprocessing info with optional disable switch
+- Automatic preprocessing info (always on in example)
ios/ImageCodeScanner.swift (3)

191-194: Limit UPC_A mapping to EAN-13 to avoid unrelated UPC-E detections

Including .upce widens results beyond the requested symbology.

-      case "UPC_A":
-        symbologies.append(contentsOf: [.ean13, .upce])  // UPC-A via EAN-13 + UPC-E
+      case "UPC_A":
+        symbologies.append(.ean13) // UPC-A is represented as EAN-13 with leading 0

109-115: Unused parameter: options

The native signature exposes options but they aren’t used; JS docs show only path/formats. Align signatures or implement options-driven toggles to avoid confusion.

Do you want me to draft a follow-up aligning iOS/Android/TS to remove or use this argument?


116-173: Verbose logging: consider DEBUG gating

The prints are helpful but noisy in production. Wrap with #if DEBUG or behind a module-level flag.

README.md (1)

55-60: Avoid promising support for unreleased RN versions

“TBD” is safer than “Fully Supported (when released)”.

-| 0.80.x+              | ✅ 0.1.x        | Fully Supported (when released)              |
+| 0.80.x+              | ✅ 0.1.x        | Planned / TBD                                |
📜 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.

📥 Commits

Reviewing files that changed from the base of the PR and between 2bd12b3 and e51fff8.

📒 Files selected for processing (5)
  • CHANGELOG.md (2 hunks)
  • README.md (13 hunks)
  • eslint.config.mjs (1 hunks)
  • example/App.tsx (1 hunks)
  • ios/ImageCodeScanner.swift (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • eslint.config.mjs
🧰 Additional context used
🧬 Code graph analysis (1)
example/App.tsx (1)
example/src/App.tsx (1)
  • App (36-257)
🪛 LanguageTool
README.md

[grammar] ~35-~35: There might be a mistake here.
Context: ...nality) ## 📋 Supported Barcode Formats | Format | iOS | Android | | ------...

(QB_NEW_EN)


[grammar] ~37-~37: There might be a mistake here.
Context: ...Formats | Format | iOS | Android | | ----------- | --- | ------- | | QR Cod...

(QB_NEW_EN)


[grammar] ~38-~38: There might be a mistake here.
Context: ...ndroid | | ----------- | --- | ------- | | QR Code | ✅ | ✅ | | Code 128...

(QB_NEW_EN)


[grammar] ~39-~39: There might be a mistake here.
Context: ... ------- | | QR Code | ✅ | ✅ | | Code 128 | ✅ | ✅ | | Code 39 ...

(QB_NEW_EN)


[grammar] ~40-~40: There might be a mistake here.
Context: ...| ✅ | | Code 128 | ✅ | ✅ | | Code 39 | ✅ | ✅ | | Code 93 ...

(QB_NEW_EN)


[grammar] ~41-~41: There might be a mistake here.
Context: ...| ✅ | | Code 39 | ✅ | ✅ | | Code 93 | ✅ | ✅ | | EAN-13 ...

(QB_NEW_EN)


[grammar] ~42-~42: There might be a mistake here.
Context: ...| ✅ | | Code 93 | ✅ | ✅ | | EAN-13 | ✅ | ✅ | | EAN-8 ...

(QB_NEW_EN)


[grammar] ~43-~43: There might be a mistake here.
Context: ...| ✅ | | EAN-13 | ✅ | ✅ | | EAN-8 | ✅ | ✅ | | UPC-A ...

(QB_NEW_EN)


[grammar] ~44-~44: There might be a mistake here.
Context: ...| ✅ | | EAN-8 | ✅ | ✅ | | UPC-A | ✅ | ✅ | | UPC-E ...

(QB_NEW_EN)


[grammar] ~45-~45: There might be a mistake here.
Context: ...| ✅ | | UPC-A | ✅ | ✅ | | UPC-E | ✅ | ✅ | | PDF417 ...

(QB_NEW_EN)


[grammar] ~46-~46: There might be a mistake here.
Context: ...| ✅ | | UPC-E | ✅ | ✅ | | PDF417 | ✅ | ✅ | | Data Mat...

(QB_NEW_EN)


[grammar] ~47-~47: There might be a mistake here.
Context: ...| ✅ | | PDF417 | ✅ | ✅ | | Data Matrix | ✅ | ✅ | | Aztec ...

(QB_NEW_EN)


[grammar] ~48-~48: There might be a mistake here.
Context: ...| ✅ | | Data Matrix | ✅ | ✅ | | Aztec | ✅ | ✅ | | ITF ...

(QB_NEW_EN)


[grammar] ~49-~49: There might be a mistake here.
Context: ...| ✅ | | Aztec | ✅ | ✅ | | ITF | ✅ | ✅ | | Codabar ...

(QB_NEW_EN)


[grammar] ~50-~50: There might be a mistake here.
Context: ...| ✅ | | ITF | ✅ | ✅ | | Codabar | ✅ | ✅ | ## Compat...

(QB_NEW_EN)


[grammar] ~55-~55: There might be a mistake here.
Context: ... | | -------------------- | ---------------...

(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: ...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] ~63-~63: There might be a mistake here.
Context: ...quirements - React Native: >=0.70.0 - React: >=17.0.0 - iOS: 13.4+ - **A...

(QB_NEW_EN)


[grammar] ~64-~64: There might be a mistake here.
Context: ...Native**: >=0.70.0 - React: >=17.0.0 - iOS: 13.4+ - Android: minSdkVersio...

(QB_NEW_EN)


[grammar] ~65-~65: There might be a mistake here.
Context: ...0 - React: >=17.0.0 - iOS: 13.4+ - Android: minSdkVersion 21+ - Node:...

(QB_NEW_EN)


[grammar] ~66-~66: There might be a mistake here.
Context: ...: 13.4+ - Android: minSdkVersion 21+ - Node: >=18 ## Installation ```bash n...

(QB_NEW_EN)


[grammar] ~177-~177: There might be a mistake here.
Context: .... Original Image: First scan attempt 2. Grayscale Conversion: Improves detecti...

(QB_NEW_EN)


[grammar] ~178-~178: There might be a mistake here.
Context: ...mproves detection in colored backgrounds 3. Contrast Enhancement: Better recogniti...

(QB_NEW_EN)


[grammar] ~179-~179: There might be a mistake here.
Context: ...etter recognition in low-contrast images 4. Rotation Attempts: Tries 0°, 90°, 180°...

(QB_NEW_EN)


[grammar] ~274-~274: There might be a mistake here.
Context: ... | Required | Description | | --------- | ------------- | -------- |...

(QB_NEW_EN)


[grammar] ~275-~275: There might be a mistake here.
Context: ...-- | -------- | ---------------------- | | options | ScanOptions | Yes |...

(QB_NEW_EN)


[grammar] ~320-~320: There might be a mistake here.
Context: ... automatically - Smart Retry Logic: If initial scan fails, automatically tries...

(QB_NEW_EN)


[grammar] ~381-~381: There might be a mistake here.
Context: ...mera and gallery using Expo Image Picker - Automatic preprocessing (always on; no m...

(QB_NEW_EN)


[grammar] ~382-~382: There might be a mistake here.
Context: ... (always on; no manual toggles required) - Multiple barcode format selection - Perf...

(QB_NEW_EN)


[grammar] ~383-~383: There might be a mistake here.
Context: ...red) - Multiple barcode format selection - Performance timing measurements - Beauti...

(QB_NEW_EN)


[grammar] ~384-~384: There might be a mistake here.
Context: ...ection - Performance timing measurements - Beautiful, responsive UI - Cross-platfor...

(QB_NEW_EN)


[grammar] ~385-~385: There might be a mistake here.
Context: ... measurements - Beautiful, responsive UI - Cross-platform support (iOS, Android, We...

(QB_NEW_EN)


[grammar] ~390-~390: There might be a mistake here.
Context: ...testing, but barcode scanning won't work - ⚡ Prebuild Mode: Full functionality ...

(QB_NEW_EN)


[grammar] ~391-~391: There might be a mistake here.
Context: ...scanning (requires Xcode/Android Studio) - 📖 **See [QUICK_START.md](./example/QUIC...

(QB_NEW_EN)


[grammar] ~392-~392: There might be a mistake here.
Context: ...RT.md)** for detailed setup instructions Platform Support: - 📱 iOS: Full ...

(QB_NEW_EN)


[grammar] ~396-~396: There might be a mistake here.
Context: ... iOS: Full camera and gallery access - 🤖 Android: Full camera and gallery ...

(QB_NEW_EN)


[grammar] ~397-~397: There might be a mistake here.
Context: ...ndroid**: Full camera and gallery access - 🌐 Web: Photo library access (camera...

(QB_NEW_EN)

CHANGELOG.md

[grammar] ~18-~18: There might be a mistake here.
Context: ...preprocessing** for optimal recognition: - Contrast enhancement - Grayscale conve...

(QB_NEW_EN)


[grammar] ~19-~19: There might be a mistake here.
Context: ...al recognition: - Contrast enhancement - Grayscale conversion - Multiple rotati...

(QB_NEW_EN)


[grammar] ~20-~20: There might be a mistake here.
Context: ...ast enhancement - Grayscale conversion - Multiple rotation attempts (0°, 90°, 180...

(QB_NEW_EN)


[grammar] ~21-~21: There might be a mistake here.
Context: ... rotation attempts (0°, 90°, 180°, 270°) - Ultra-simple API - just pass image pat...

(QB_NEW_EN)


[grammar] ~32-~32: There might be a mistake here.
Context: ...ght and performant native implementation - Automatic preprocessing enabled by def...

(QB_NEW_EN)


[grammar] ~33-~33: 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] ~34-~34: There might be a mistake here.
Context: ...th multiple image enhancement techniques - No additional setup required for Android...

(QB_NEW_EN)


[grammar] ~35-~35: There might be a mistake here.
Context: ...No additional setup required for Android - Minimal iOS setup with just pod install ...

(QB_NEW_EN)


[grammar] ~36-~36: There might be a mistake here.
Context: ... Minimal iOS setup with just pod install - Expo integration with proper prebuild ...

(QB_NEW_EN)


[grammar] ~37-~37: There might be a mistake here.
Context: ...egration** with proper prebuild workflow - Modern example app using Expo Image Pi...

(QB_NEW_EN)


[grammar] ~38-~38: There might be a mistake here.
Context: ...** using Expo Image Picker and StatusBar - Simplified API - just pass image path ...

(QB_NEW_EN)


[grammar] ~39-~39: There might be a mistake here.
Context: ...API** - just pass image path and formats - Performance metrics and timing measure...

(QB_NEW_EN)


[grammar] ~44-~44: There might be a mistake here.
Context: ... - Modern Expo-based example application - Barcode format selection UI with real-ti...

(QB_NEW_EN)


[grammar] ~45-~45: There might be a mistake here.
Context: ...rmat selection UI with real-time toggles - Automatic preprocessing info with option...

(QB_NEW_EN)


[grammar] ~46-~46: There might be a mistake here.
Context: ...essing info with optional disable switch - Improved error handling and user feedbac...

(QB_NEW_EN)

Comment thread ios/ImageCodeScanner.swift

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
android/src/main/java/com/imagecodescanner/ImageCodeScannerModule.kt (1)

206-213: Recycle bitmaps to prevent leaks; also clean up pending variants after success.

Bitmaps are precomputed and kept in memory; recycle each after use and recycle remaining ones when a result is found.

       fun tryNextImage() {
         if (currentIndex >= imagesToTry.size) {
           // No more images to try, return empty result
           val arr = Arguments.fromList(emptyList<String>())
           promise.resolve(arr)
           return
         }
@@
-        val image = InputImage.fromBitmap(currentBitmap, 0)
-        val scanner = BarcodeScanning.getClient(scannerOptions)
+        val image = InputImage.fromBitmap(currentBitmap, 0)
+        val scanner = BarcodeScanning.getClient(scannerOptions)
@@
           .addOnSuccessListener { barcodes ->
             try {
               if (barcodes.isNotEmpty()) {
                 // Found barcodes, process and return
                 val codes = barcodes
                   .mapNotNull { barcode -> 
                     val value = barcode.displayValue ?: barcode.rawValue
                     value
                   }
                   .filter { it.isNotEmpty() }
                 
-                val arr = Arguments.fromList(codes)
-                promise.resolve(arr)
+                val arr = Arguments.fromList(codes)
+                // Recycle remaining, unprocessed bitmaps
+                imagesToTry.drop(currentIndex).forEach { (_, bmp) ->
+                  if (!bmp.isRecycled) bmp.recycle()
+                }
+                promise.resolve(arr)
               } else {
                 // No barcodes found, try next preprocessing
                 tryNextImage()
               }
             } catch (e: Exception) {
               android.util.Log.e("ImageCodeScanner", "Error processing results for $description", e)
               tryNextImage()
             } finally {
               scanner.close()
+              if (!currentBitmap.isRecycled) {
+                currentBitmap.recycle()
+              }
             }
           }
           .addOnFailureListener { exception ->
             android.util.Log.e("ImageCodeScanner", "Scan failed for $description: ${exception.message}")
             scanner.close()
+            if (!currentBitmap.isRecycled) {
+              currentBitmap.recycle()
+            }
             tryNextImage()
           }

Also applies to: 221-249

🧹 Nitpick comments (6)
package.json (1)

105-106: Tighten peer dependency ranges to reflect tested support.
"react: >=17" and "react-native: >=0.70" are very broad and may accidentally accept React 19 or older RN where Turbo Modules/Codegen differ. Suggest narrowing to your validated band and documenting it in COMPATIBILITY.md.

Apply if accurate for your tests:

   "peerDependencies": {
-    "react": ">=17.0.0",
-    "react-native": ">=0.70.0"
+    "react": ">=18 <19",
+    "react-native": ">=0.72 <0.80"
   }
android/build.gradle (2)

62-63: Java 17 upgrade: good; consider Gradle toolchains for portability.

Compile/target 17 and jvmTarget=17 are fine. Optionally add Gradle toolchains to avoid local JDK drift in consumer apps.

Apply:

 android {
+  java {
+    toolchain {
+      languageVersion = JavaLanguageVersion.of(17)
+    }
+  }
   compileOptions {
     sourceCompatibility JavaVersion.VERSION_17
     targetCompatibility JavaVersion.VERSION_17
   }
   kotlinOptions {
     jvmTarget = "17"
   }

Also applies to: 67-67


73-75: Duplicate source directory entry for codegen; keep one to avoid redundant scanning.

Both paths resolve to the same location. Drop the duplicate.

   sourceSets {
     main {
       java.srcDirs += [
-        "${buildDir}/generated/source/codegen/java",
-        "build/generated/source/codegen/java"
+        "${buildDir}/generated/source/codegen/java"
       ]
     }
   }
android/src/main/java/com/imagecodescanner/ImageCodeScannerModule.kt (3)

175-202: Respect options parameter; avoid always doing all preprocess steps.

Current code ignores options and forces all transforms, which costs CPU/memory. Gate by options with sensible defaults (true to preserve behavior).

-      val imagesToTry = mutableListOf<Pair<String, Bitmap>>()
-      imagesToTry.add("Original" to bitmap)
+      val imagesToTry = mutableListOf<Pair<String, Bitmap>>()
+      imagesToTry.add("Original" to bitmap)
+
+      val grayscaleEnabled = if (options.hasKey("convertToGrayscale")) options.getBoolean("convertToGrayscale") else true
+      val enhanceContrastEnabled = if (options.hasKey("enhanceContrast")) options.getBoolean("enhanceContrast") else true
+      val tryRotationsEnabled = if (options.hasKey("tryRotations")) options.getBoolean("tryRotations") else true
@@
-      // Always add grayscale version
-      try {
-        imagesToTry.add("Grayscale" to convertToGrayscale(bitmap))
-        android.util.Log.d("ImageCodeScanner", "Added grayscale version")
-      } catch (e: Exception) {
-        android.util.Log.w("ImageCodeScanner", "Failed to create grayscale: ${e.message}")
-      }
+      if (grayscaleEnabled) {
+        try {
+          imagesToTry.add("Grayscale" to convertToGrayscale(bitmap))
+          android.util.Log.d("ImageCodeScanner", "Added grayscale version")
+        } catch (e: Exception) {
+          android.util.Log.w("ImageCodeScanner", "Failed to create grayscale: ${e.message}")
+        }
+      }
@@
-      // Always add enhanced contrast version
-      try {
-        imagesToTry.add("Enhanced contrast" to enhanceContrast(bitmap))
-        android.util.Log.d("ImageCodeScanner", "Added contrast enhanced version")
-      } catch (e: Exception) {
-        android.util.Log.w("ImageCodeScanner", "Failed to enhance contrast: ${e.message}")
-      }
+      if (enhanceContrastEnabled) {
+        try {
+          imagesToTry.add("Enhanced contrast" to enhanceContrast(bitmap))
+          android.util.Log.d("ImageCodeScanner", "Added contrast enhanced version")
+        } catch (e: Exception) {
+          android.util.Log.w("ImageCodeScanner", "Failed to enhance contrast: ${e.message}")
+        }
+      }
@@
-      // Always add rotated versions
-      try {
-        imagesToTry.add("Rotated 90°" to rotateBitmap(bitmap, 90f))
-        imagesToTry.add("Rotated 180°" to rotateBitmap(bitmap, 180f))
-        imagesToTry.add("Rotated 270°" to rotateBitmap(bitmap, 270f))
-        android.util.Log.d("ImageCodeScanner", "Added rotated versions")
-      } catch (e: Exception) {
-        android.util.Log.w("ImageCodeScanner", "Failed to rotate: ${e.message}")
-      }
+      if (tryRotationsEnabled) {
+        try {
+          imagesToTry.add("Rotated 90°" to rotateBitmap(bitmap, 90f))
+          imagesToTry.add("Rotated 180°" to rotateBitmap(bitmap, 180f))
+          imagesToTry.add("Rotated 270°" to rotateBitmap(bitmap, 270f))
+          android.util.Log.d("ImageCodeScanner", "Added rotated versions")
+        } catch (e: Exception) {
+          android.util.Log.w("ImageCodeScanner", "Failed to rotate: ${e.message}")
+        }
+      }

If your TS API intentionally removed user-tunable preprocessing, ignore this and keep all-true defaults.


217-219: Performance: reuse a single BarcodeScanner instance across attempts.

Creating/closing a scanner per attempt adds overhead. Hoist val scanner = BarcodeScanning.getClient(scannerOptions) outside tryNextImage() and close it once when done.


217-219: Orientation handling: prefer EXIF-aware rotation for the original image.

Using InputImage.fromBitmap(..., 0) ignores EXIF. Consider reading EXIF via ExifInterface and rotating the original once instead of trying fixed 90° steps.

Also applies to: 95-99

📜 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.

📥 Commits

Reviewing files that changed from the base of the PR and between e51fff8 and 818b5aa.

📒 Files selected for processing (5)
  • RNImageCodeScanner.podspec (1 hunks)
  • android/build.gradle (1 hunks)
  • android/src/main/java/com/imagecodescanner/ImageCodeScannerModule.kt (2 hunks)
  • package.json (2 hunks)
  • react-native.config.js (1 hunks)
🔇 Additional comments (4)
package.json (2)

4-4: Description change LGTM; keep tagline consistent across docs.
Matches the simplified API direction. Ensure README, npm page, and repo description use the same phrasing.


84-102: DevDeps Alignment with RN 0.79/React 18 Verified

All of the updated development dependencies are compatible and no changes are required:

  • ESLint 9.22.0 and eslint-config-prettier 9.1.0 are intended to be used together (v9 of the Prettier config supports ESLint v9).
  • TypeScript ^5.3.0 is within the supported range for React 18 and React Native 0.79.x (the RN codegen tooling and tsconfig presets are fully compatible).

No further action needed.

react-native.config.js (1)

1-13: Confirm Autolinking Configuration
The react-native.config.js at lines 1–13 looks correct for both iOS (podspecPath) and Android (sourceDir/manifestPath). To ensure the React Native CLI actually picks it up:

• Add the CLI if missing
• In your package.json, under devDependencies, include:
json "@react-native-community/cli": "latest"
• Re-run config and inspect the specific entry

npx react-native config \
  | jq '.dependencies["react-native-image-code-scanner"]'

You should see an object containing your podspecPath and sourceDir settings.

If that key is absent or empty, please verify your module’s name in its podspec/package.json and adjust accordingly.

android/src/main/java/com/imagecodescanner/ImageCodeScannerModule.kt (1)

10-11: Base class switch to generated spec: LGTM.

Aligns with codegen-based modules and keeps the JS name via ReactModule annotation.

Comment on lines +115 to 130
val bitmapOptions = BitmapFactory.Options()
bitmapOptions.inJustDecodeBounds = true
BitmapFactory.decodeFile(imgFile.absolutePath, bitmapOptions)

// Calculate sample size if image is too large
var sampleSize = 1
val maxDimension = 2048
while (options.outWidth / sampleSize > maxDimension || options.outHeight / sampleSize > maxDimension) {
while (bitmapOptions.outWidth / sampleSize > maxDimension || bitmapOptions.outHeight / sampleSize > maxDimension) {
sampleSize *= 2
}

options.inJustDecodeBounds = false
options.inSampleSize = sampleSize
bitmapOptions.inJustDecodeBounds = false
bitmapOptions.inSampleSize = sampleSize

val originalBitmap = BitmapFactory.decodeFile(imgFile.absolutePath, options)
val originalBitmap = BitmapFactory.decodeFile(imgFile.absolutePath, bitmapOptions)
if (originalBitmap == null) {

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Bitmap sampling: add safe defaults and guard decode-bounds edge cases.

Set preferred config and recycle the original when a scaled copy is used to cut peak memory.

-      val bitmapOptions = BitmapFactory.Options()
-      bitmapOptions.inJustDecodeBounds = true
+      val bitmapOptions = BitmapFactory.Options().apply {
+        inJustDecodeBounds = true
+        inPreferredConfig = Bitmap.Config.ARGB_8888
+      }
       BitmapFactory.decodeFile(imgFile.absolutePath, bitmapOptions)
@@
-      bitmapOptions.inJustDecodeBounds = false
-      bitmapOptions.inSampleSize = sampleSize
+      bitmapOptions.inJustDecodeBounds = false
+      bitmapOptions.inSampleSize = sampleSize.coerceAtLeast(1)
@@
-      val originalBitmap = BitmapFactory.decodeFile(imgFile.absolutePath, bitmapOptions)
+      val originalBitmap = BitmapFactory.decodeFile(imgFile.absolutePath, bitmapOptions)
       if (originalBitmap == null) {
         promise.reject("INVALID_IMAGE", "Cannot decode image file: $path", null)
         return
       }
@@
-      val bitmap = scaleBitmapIfNeeded(originalBitmap)
+      val bitmap = scaleBitmapIfNeeded(originalBitmap)
+      if (bitmap !== originalBitmap && !originalBitmap.isRecycled) {
+        originalBitmap.recycle()
+      }
📝 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.

Suggested change
val bitmapOptions = BitmapFactory.Options()
bitmapOptions.inJustDecodeBounds = true
BitmapFactory.decodeFile(imgFile.absolutePath, bitmapOptions)
// Calculate sample size if image is too large
var sampleSize = 1
val maxDimension = 2048
while (options.outWidth / sampleSize > maxDimension || options.outHeight / sampleSize > maxDimension) {
while (bitmapOptions.outWidth / sampleSize > maxDimension || bitmapOptions.outHeight / sampleSize > maxDimension) {
sampleSize *= 2
}
options.inJustDecodeBounds = false
options.inSampleSize = sampleSize
bitmapOptions.inJustDecodeBounds = false
bitmapOptions.inSampleSize = sampleSize
val originalBitmap = BitmapFactory.decodeFile(imgFile.absolutePath, options)
val originalBitmap = BitmapFactory.decodeFile(imgFile.absolutePath, bitmapOptions)
if (originalBitmap == null) {
val bitmapOptions = BitmapFactory.Options().apply {
inJustDecodeBounds = true
inPreferredConfig = Bitmap.Config.ARGB_8888
}
BitmapFactory.decodeFile(imgFile.absolutePath, bitmapOptions)
// Calculate sample size if image is too large
var sampleSize = 1
val maxDimension = 2048
while (bitmapOptions.outWidth / sampleSize > maxDimension || bitmapOptions.outHeight / sampleSize > maxDimension) {
sampleSize *= 2
}
bitmapOptions.inJustDecodeBounds = false
bitmapOptions.inSampleSize = sampleSize.coerceAtLeast(1)
val originalBitmap = BitmapFactory.decodeFile(imgFile.absolutePath, bitmapOptions)
if (originalBitmap == null) {
promise.reject("INVALID_IMAGE", "Cannot decode image file: $path", null)
return
}
val bitmap = scaleBitmapIfNeeded(originalBitmap)
if (bitmap !== originalBitmap && !originalBitmap.isRecycled) {
originalBitmap.recycle()
}
🤖 Prompt for AI Agents
In android/src/main/java/com/imagecodescanner/ImageCodeScannerModule.kt around
lines 115 to 130, the Bitmap decoding lacks safe defaults and edge-case guards:
set bitmapOptions.inPreferredConfig to Bitmap.Config.RGB_565 (or ARGB_8888 if
alpha needed) before decoding, protect against decodeFile returning 0 for
outWidth/outHeight by treating them as unknown (skip sampling loop or clamp
sampleSize to 1 if bounds are invalid), and after creating a scaled/copy bitmap
ensure the originalBitmap is recycled if it is not the same instance to free
memory; also set bitmapOptions.inMutable as needed for further operations.
Ensure sampleSize is at least 1 and cap it to avoid integer overflow.


Pod::Spec.new do |s|
s.name = "ImageCodeScanner"
s.name = "RNImageCodeScanner"

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Verification agent

🧩 Analysis chain

Pod rename aligns with autolinking; verify tags exist for CocoaPods fetch.

Rename to RNImageCodeScanner looks correct and matches react-native.config.js. Ensure a matching git tag exists for each version so pod install can resolve the source.

Run:


🏁 Script executed:

#!/bin/bash
# Confirm the config points to the new podspec name and the version tag exists
rg -n "podspecPath: './RNImageCodeScanner.podspec'"
git tag -l | rg -n "$(jq -r .version package.json)"

Length of output: 195


🏁 Script executed:

#!/bin/bash
# Verify if any git tags exist matching the version from package.json
version=$(jq -r .version package.json)
echo "Checking for git tags matching version '${version}'..."
git tag -l "*${version}*"

Length of output: 184


Action Required: Add Git tag for version 0.1.0

  • The RNImageCodeScanner.podspec rename aligns correctly with the entry in react-native.config.js at line 5.
  • No Git tags matching the current package version (0.1.0) were found. A matching tag is required so that CocoaPods can resolve the source when running pod install.
  • Please create and push a tag for this version (e.g. v0.1.0) in the repository.
🤖 Prompt for AI Agents
In RNImageCodeScanner.podspec around line 6, the podspec version (0.1.0) has no
matching Git tag so CocoaPods cannot resolve the source; create and push a Git
tag that exactly matches the package version (for example v0.1.0) on the
repository, ensure the tag is pushed to the remote (origin) and points to the
commit that contains this podspec, and then verify by running a local pod
install or fetching tags so CocoaPods can resolve the source.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

♻️ Duplicate comments (4)
ios/ImageCodeScanner.swift (4)

14-16: Good reuse of CIContext to cut per-call overhead.
Re-using a shared CIContext avoids repeated GPU state setup. Nice.


30-33: Preserving scale and orientation on processed images is correct.
This keeps Vision orientation mapping accurate across preprocessing steps.

Also applies to: 47-50


146-173: Good: preprocessing operates on the scaled base image.
Prevents full‑res processing spikes and keeps the pipeline consistent.


177-208: UPC_A format mapping is too broad; drop UPC‑E from UPC_A.
Users asking for UPC_A shouldn’t receive UPC‑E by default.

-      case "UPC_A":
-        symbologies.append(contentsOf: [.ean13, .upce])  // UPC-A via EAN-13 + UPC-E
+      case "UPC_A":
+        symbologies.append(.ean13)  // UPC-A is encoded within EAN-13
🧹 Nitpick comments (11)
ios/ImageCodeScanner.swift (6)

23-26: Prefer typed Core Image APIs or keyed constants over string keys.
Reduces key-typo risk and improves readability.

Example using CIFilter builtins (iOS 13+):

-    let filter = CIFilter(name: "CIColorControls")
-    filter?.setValue(currentCIImage, forKey: "inputImage")
-    filter?.setValue(2.0, forKey: "inputContrast")
-    filter?.setValue(-0.2, forKey: "inputBrightness")
-    filter?.setValue(1.0, forKey: "inputSaturation")
+    let filter = CIFilter.colorControls()
+    filter.inputImage = currentCIImage
+    filter.contrast = 2.0
+    filter.brightness = -0.2
+    filter.saturation = 1.0

If you can’t adopt builtins, at least use kCI* keyed constants:

-    filter?.setValue(currentCIImage, forKey: "inputImage")
+    filter?.setValue(currentCIImage, forKey: kCIInputImageKey)

Also applies to: 39-44


52-78: Rotate using UIGraphicsImageRenderer for consistency and clarity.
Aligns with the renderer used elsewhere and keeps scale handling uniform.

-    UIGraphicsBeginImageContextWithOptions(newSize, false, image.scale)
-    defer { UIGraphicsEndImageContext() }
-    guard let context = UIGraphicsGetCurrentContext() else { return nil }
+    let format = UIGraphicsImageRendererFormat.default()
+    format.scale = image.scale
+    let renderer = UIGraphicsImageRenderer(size: newSize, format: format)
-    context.translateBy(x: newSize.width / 2, y: newSize.height / 2)
-    context.rotate(by: radians)
-    image.draw(in: CGRect(
-      x: -image.size.width / 2,
-      y: -image.size.height / 2,
-      width: image.size.width,
-      height: image.size.height
-    ))
-    return UIGraphicsGetImageFromCurrentImageContext()
+    return renderer.image { ctx in
+      let c = ctx.cgContext
+      c.translateBy(x: newSize.width / 2, y: newSize.height / 2)
+      c.rotate(by: radians)
+      image.draw(in: CGRect(x: -image.size.width/2, y: -image.size.height/2,
+                            width: image.size.width, height: image.size.height))
+    }

80-93: Minor: consider point-sized renderer with image.scale to avoid “size” mismatches.
Current code renders in pixel coords (format.scale=1) then assigns the original scale, which can make UIImage.size (in points) less intuitive. Optional tweak below keeps 1:1 between size and scale.

-    let newSizePx = CGSize(width: newW, height: newH)
-    let format = UIGraphicsImageRendererFormat.default()
-    format.scale = 1 // render size is in pixels
-    let rendered = UIGraphicsImageRenderer(size: newSizePx, format: format).image { _ in
-      image.draw(in: CGRect(origin: .zero, size: newSizePx))
-    }
-    return UIImage(cgImage: rendered.cgImage!, scale: image.scale, orientation: image.imageOrientation)
+    let newSizePt = CGSize(width: newW / image.scale, height: newH / image.scale)
+    let format = UIGraphicsImageRendererFormat.default()
+    format.scale = image.scale
+    let rendered = UIGraphicsImageRenderer(size: newSizePt, format: format).image { _ in
+      image.draw(in: CGRect(origin: .zero, size: newSizePt))
+    }
+    return UIImage(cgImage: rendered.cgImage!, scale: image.scale, orientation: image.imageOrientation)

162-173: Generate rotated/derived images lazily to cut peak memory.
Prebuilding 6–7 large variants can exceed 100MB transiently. Build on demand via closures.

-    var imagesToTry: [(String, UIImage)] = [("Original", baseImage)]
-    if let grayscaleImage = convertToGrayscale(baseImage) {
-      imagesToTry.append(("Grayscale", grayscaleImage))
-    }
-    if let contrastImage = enhanceContrast(baseImage) {
-      imagesToTry.append(("Enhanced contrast", contrastImage))
-    }
-    if let rotated90 = rotateImage(baseImage, degrees: 90) { imagesToTry.append(("Rotated 90°", rotated90)) }
-    if let rotated180 = rotateImage(baseImage, degrees: 180) { imagesToTry.append(("Rotated 180°", rotated180)) }
-    if let rotated270 = rotateImage(baseImage, degrees: 270) { imagesToTry.append(("Rotated 270°", rotated270)) }
+    var imagesToTry: [(String, () -> UIImage?)] = []
+    imagesToTry.append(("Original", { baseImage }))
+    imagesToTry.append(("Grayscale", { self.convertToGrayscale(baseImage) }))
+    imagesToTry.append(("Enhanced contrast", { self.enhanceContrast(baseImage) }))
+    imagesToTry.append(("Rotated 90°", { self.rotateImage(baseImage, degrees: 90) }))
+    imagesToTry.append(("Rotated 180°", { self.rotateImage(baseImage, degrees: 180) }))
+    imagesToTry.append(("Rotated 270°", { self.rotateImage(baseImage, degrees: 270) }))

And adapt tryScanning to invoke closures:

-    func tryScanning(images: [(String, UIImage)], index: Int) {
+    func tryScanning(images: [(String, () -> UIImage?)], index: Int) {
@@
-      let (description, currentImage) = images[index]
+      let (description, makeImage) = images[index]
+      guard let currentImage = makeImage() else {
+        tryScanning(images: images, index: index + 1)
+        return
+      }

Also applies to: 215-233


174-213: Deduplicate symbologies before assigning to the request.
Prevents redundant work and simplifies debugging.

     // If no formats specified, default to QR
     if symbologies.isEmpty {
       symbologies.append(.qr)
     }
+    // Deduplicate
+    symbologies = Array(Set(symbologies))

116-173: Gate debug logs or switch to OSLog.
Reduces console noise in production apps.

Example:

-    print("ImageCodeScanner iOS - Starting scan with all preprocessing options enabled")
+    #if DEBUG
+    print("ImageCodeScanner iOS - Starting scan with all preprocessing options enabled")
+    #endif

Also applies to: 225-261, 284-286

.github/actions/setup/action.yml (2)

20-24: Trim cache scope to Yarn cache + install-state; avoid node_modules caching

Caching **/node_modules is large and brittle across runners; Yarn’s cache + install-state is sufficient and faster.

         path: |
-          **/node_modules
           .yarn/cache
           .yarn/install-state.gz
         path: |
-          **/node_modules
           .yarn/cache
           .yarn/install-state.gz

Also applies to: 42-45


29-31: Consider always running yarn install even on cache hits

Even with a restored install state, yarn install --immutable is cheap and verifies integrity/lifecycle scripts. Optional, but improves safety.

-    - name: Install dependencies
-      if: steps.yarn-cache.outputs.cache-hit != 'true'
+    - name: Install dependencies
+      # Run even on cache hits to verify state; Yarn no-ops when up-to-date
.github/workflows/ci.yml (3)

109-114: Android build memory: prefer GRADLE_OPTS over JAVA_OPTS

Gradle honors GRADLE_OPTS/org.gradle.jvmargs. Add GRADLE_OPTS for clearer effect.

       - name: Build example for Android
         env:
-          JAVA_OPTS: '-XX:MaxHeapSize=6g'
+          JAVA_OPTS: '-XX:MaxHeapSize=6g'
+          GRADLE_OPTS: '-Xmx6g -XX:MaxMetaspaceSize=1g'
         run: |
           yarn turbo run build:android --cache-dir="${{ env.TURBO_CACHE_DIR }}"

160-167: Streamline Pods install; avoid unconditional repo update

Use --repo-update with pod install and set Bundler path for cache affinity.

       run: |
         cd example
-        bundle install
-        bundle exec pod repo update --verbose
-        bundle exec pod install --project-directory=ios
+        bundle config set path 'vendor/bundle'
+        bundle install --jobs 4 --retry 3
+        bundle exec pod install --project-directory=ios --repo-update

149-159: CI cache cleanup: remove unused Bundler path or configure Bundler
We’ve confirmed there is no Gemfile/Gemfile.lock nor any Bundler config under example/, so example/vendor/bundle is never populated and ends up as an empty cache entry. To keep the cache step accurate, either remove the Bundler path entirely or, if you intend to use Bundler, add a Gemfile/Gemfile.lock, configure bundle install --path vendor/bundle, and include the lockfile in your cache key.

• File: .github/workflows/ci.yml (lines 149–159)
• Action: adjust the cache path (and optionally the key if Bundler is added)

Suggested optional diff:

       with:
         path: |
-          example/ios/Pods
-          example/vendor/bundle
+          example/ios/Pods
         key: ${{ runner.os }}-cocoapods-${{ hashFiles('example/ios/Podfile.lock') }}
📜 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.

📥 Commits

Reviewing files that changed from the base of the PR and between 818b5aa and ff08656.

📒 Files selected for processing (3)
  • .github/actions/setup/action.yml (1 hunks)
  • .github/workflows/ci.yml (3 hunks)
  • ios/ImageCodeScanner.swift (1 hunks)
🧰 Additional context used
🧬 Code graph analysis (1)
ios/ImageCodeScanner.swift (1)
android/src/main/java/com/imagecodescanner/ImageCodeScannerModule.kt (4)
  • convertToGrayscale (77-93)
  • name (19-263)
  • enhanceContrast (48-75)
  • scanFromPath (101-258)
🔇 Additional comments (3)
ios/ImageCodeScanner.swift (2)

118-136: Promise resolution guard is solid.
Simple, thread-safe single-resolve pattern. LGTM.


265-267: Confirm necessity of forcing Revision1 on Simulator.
May reduce accuracy; use only if newer revisions crash on sim.

Would you like me to check Apple’s latest Vision revision behavior on the simulator and propose a conditional based on iOS version?

.github/workflows/ci.yml (1)

13-17: Good: global immutable Yarn + increased Node memory

Top-level env aligns with deterministic installs and prevents OOMs.

Comment on lines +12 to +15
- name: Enable Corepack
run: corepack enable
shell: bash

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Verification agent

🧩 Analysis chain

Corepack version pin: verify packageManager is set

Ensure package.json (and example/package.json if applicable) has a packageManager: "yarn@<exact>" entry so corepack enable activates the intended Yarn version.


🏁 Script executed:

#!/bin/bash
# Inspect packageManager fields
jq -r '.packageManager // "MISSING"' package.json 2>/dev/null || true
jq -r '.packageManager // "MISSING"' example/package.json 2>/dev/null || true

Length of output: 130


Add missing packageManager field to example/package.json

The root package.json already specifies "packageManager": "yarn@3.6.1", but example/package.json is missing this field. Without it, corepack enable may not pick up the intended Yarn version when running the example workflow.

• In example/package.json, add:

  "packageManager": "yarn@3.6.1"

to match the root configuration and ensure Corepack correctly activates Yarn v3.6.1.

🤖 Prompt for AI Agents
In .github/actions/setup/action.yml around lines 12 to 15: update the example
project's package.json to include the missing packageManager field so Corepack
uses the intended Yarn version; open example/package.json and add
"packageManager": "yarn@3.6.1" at the top-level of the JSON (alongside
name/version/etc.) to match the root package.json and ensure corepack enable
picks Yarn v3.6.1.

Comment thread .github/actions/setup/action.yml

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

📜 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.

📥 Commits

Reviewing files that changed from the base of the PR and between ff08656 and 34a7f3d.

⛔ Files ignored due to path filters (1)
  • yarn.lock is excluded by !**/yarn.lock, !**/*.lock
📒 Files selected for processing (2)
  • .github/actions/setup/action.yml (1 hunks)
  • example/package.json (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • .github/actions/setup/action.yml
🔇 Additional comments (1)
example/package.json (1)

20-21: Confirm example package dependency updates; please verify SDK 52 in root and run Expo Doctor

We’ve confirmed that example/package.json has been updated as intended. Please take the following steps to ensure full Expo SDK 52 alignment:

  • Verified in example/package.json (lines 20–21):
        "expo-image-picker": "~16.0.3",
        "expo-status-bar": "~2.0.1",
    
  • Ensure your root package.json specifies Expo SDK 52 (e.g. "expo": "~52.0.0").
  • Run npx expo doctor in your local environment to validate compatibility and catch any mismatch warnings.

Comment thread example/package.json
"react": "18.3.1",
"react-native": "0.76.3",
"react-native-image-code-scanner": "file:../"
"react-native-image-code-scanner": "workspace:*"

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Verification agent

🧩 Analysis chain

workspace: is correct; ensure workspace wiring and CI install strategy.*

Confirm the lib is part of the root workspaces and that CI runs yarn install at the repo root (not in example/) to use the single lockfile.


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# 1) Root lockfile present
test -f yarn.lock && echo "OK: root yarn.lock present"

# 2) Example has no nested lockfile
test ! -f example/yarn.lock && echo "OK: no example/yarn.lock"

# 3) Root workspaces declare example and the library
echo "Root workspaces:"
jq -r 'if (.workspaces|type)=="array" then .workspaces[] else .workspaces.packages[] end' package.json

# 4) The example depends on the local workspace
rg -nP '"react-native-image-code-scanner"\s*:\s*"workspace:\*"' example/package.json || { echo "Missing workspace:* dep"; exit 1; }

# 5) The library package exists in the repo
rg -nP '"name"\s*:\s*"react-native-image-code-scanner"' -g '!**/node_modules/**'

echo "If 3–5 look good, CI should install from root with --immutable using the single lockfile."

Length of output: 824


Add the library to your root workspaces and verify CI installs from repo root

It looks like your root package.json’s workspaces only lists example, so the react-native-image-code-scanner package won’t be linked as a workspace. You’ll need to:

• In the repo-root package.json, update the workspaces field to include the library’s directory alongside example (e.g.

{
  "workspaces": [
    "example",
    "path/to/react-native-image-code-scanner"
  ]
}

)
• Confirm that the CI pipeline runs yarn install (ideally with --immutable) from the repository root—not within example/—so both workspaces share the single lockfile.
• After updating, you should see both entries when running:

jq -r '.workspaces[]' package.json

and the library will resolve via the workspace protocol.

🤖 Prompt for AI Agents
In example/package.json around line 23, the dependency uses the workspace
protocol but the repo-root package.json workspaces only list "example", so the
library won't be linked as a workspace; update the repo-root package.json
"workspaces" array to include the react-native-image-code-scanner package
directory alongside "example" (e.g. add its relative path), ensure CI runs yarn
install from the repository root (preferably yarn install --immutable) so the
single lockfile is used, and verify by running jq -r '.workspaces[]'
package.json at the repo root to confirm both entries are present and the
package resolves via the workspace protocol.

@anngth anngth changed the title fix: add yarn.lock for deterministic CI builds fix: resolve CI and Android build issues with Yarn lockfile and JVM compatibility Aug 27, 2025
Heimer Nguyen added 5 commits August 28, 2025 01:42
- Add react-native@0.76.9 as explicit dependency (required for Expo SDK 52)
- Remove unsupported --non-interactive flag from build scripts
- Rely on CI=1 environment variable set in GitHub Actions workflow
- This fixes the Input is required error during CI builds
…uild

- Move pod install into the build:ios script after expo prebuild
- Remove separate CocoaPods installation step from workflow
- This ensures the ios directory exists before trying to install pods
- Fixes the "is not a valid directory" error in CI

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
.github/workflows/ci.yml (2)

85-91: Quote bug breaks Node expression; switch to jq or fix quoting.

The outer double quotes conflict with the inner ones, risking a syntax error in node -p. Prefer jq for robustness.

Option A (recommended, jq):

-          TURBO_CACHE_STATUS=$(node -p "($(yarn turbo run build:android --cache-dir="${{ env.TURBO_CACHE_DIR }}" --dry=json)).tasks.find(t => t.task === 'build:android').cache.status")
+          TURBO_CACHE_STATUS=$(yarn turbo run build:android --cache-dir="${{ env.TURBO_CACHE_DIR }}" --dry=json \
+            | jq -r '.tasks[] | select(.task=="build:android") | .cache.status')

Option B (fix quoting with single-quoted outer string):

-          TURBO_CACHE_STATUS=$(node -p "($(yarn turbo run build:android --cache-dir="${{ env.TURBO_CACHE_DIR }}" --dry=json)).tasks.find(t => t.task === 'build:android').cache.status")
+          TURBO_CACHE_STATUS=$(node -p '($(yarn turbo run build:android --cache-dir="${{ env.TURBO_CACHE_DIR }}" --dry=json)).tasks.find(t => t.task === "build:android").cache.status')

149-154: Repeat the Android quote issue for iOS cache check.

Apply the same jq (or quoting) fix to avoid breaking node -p.

-          TURBO_CACHE_STATUS=$(node -p "($(yarn turbo run build:ios --cache-dir="${{ env.TURBO_CACHE_DIR }}" --dry=json)).tasks.find(t => t.task === 'build:ios').cache.status")
+          TURBO_CACHE_STATUS=$(yarn turbo run build:ios --cache-dir="${{ env.TURBO_CACHE_DIR }}" --dry=json \
+            | jq -r '.tasks[] | select(.task=="build:ios") | .cache.status')
♻️ Duplicate comments (1)
example/package.json (1)

24-24: workspace: is correct; ensure workspace wiring and CI install strategy.*

Root package.json must include both the example and the library in “workspaces”, and CI should run yarn install from the repo root using the single lockfile.

Run:

#!/bin/bash
set -euo pipefail

echo "1) Root lockfile:"
test -f yarn.lock && echo "OK: root yarn.lock present" || { echo "Missing root yarn.lock"; exit 1; }

echo "2) No nested lockfile in example/:"
test ! -f example/yarn.lock && echo "OK: no example/yarn.lock" || { echo "Found example/yarn.lock"; exit 1; }

echo "3) Root workspaces list:"
jq -r 'if (.workspaces|type)=="array" then .workspaces[] else .workspaces.packages[] end' package.json

echo "4) Example depends on the library via workspace protocol:"
rg -nP '"react-native-image-code-scanner"\\s*:\\s*"workspace:\\*"' example/package.json || { echo "Missing workspace:* dep"; exit 1; }

echo "5) Library package detected in repo:"
rg -nP '"name"\\s*:\\s*"react-native-image-code-scanner"' -g '!**/node_modules/**'
🧹 Nitpick comments (7)
example/package.json (1)

15-16: Harden CI build scripts: add Gradle flags and disable iOS code signing.

Add --no-daemon/--stacktrace for Gradle reliability and CODE_SIGNING_ALLOWED=NO to avoid CI signing prompts.

Apply this diff:

-"build:android": "expo prebuild --platform android && cd android && ./gradlew assembleDebug assembleAndroidTest -DtestBuildType=debug",
+"build:android": "expo prebuild --platform android && cd android && ./gradlew --no-daemon --stacktrace assembleDebug assembleAndroidTest -DtestBuildType=debug",
-"build:ios": "expo prebuild --platform ios && cd ios && xcodebuild -workspace ImageCodeScannerExample.xcworkspace -scheme ImageCodeScannerExample -configuration Debug -sdk iphonesimulator -derivedDataPath build"
+"build:ios": "expo prebuild --platform ios && cd ios && xcodebuild -workspace ImageCodeScannerExample.xcworkspace -scheme ImageCodeScannerExample -configuration Debug -sdk iphonesimulator -derivedDataPath build CODE_SIGNING_ALLOWED=NO"

Also verify the workspace/scheme name “ImageCodeScannerExample” matches the generated Xcode project after prebuild.

.github/workflows/ci.yml (6)

71-75: Redundant NODE_OPTIONS override; rely on the top-level env.

Since Lines 13–17 already set NODE_OPTIONS, you can drop this per-step override to reduce noise.

-      - name: Install example dependencies
-        run: cd example && yarn install --immutable
-        env:
-          NODE_OPTIONS: --max-old-space-size=4096
+      - name: Install example dependencies
+        run: cd example && yarn install --immutable

117-119: Prefer GRADLE_OPTS/org.gradle.jvmargs over JAVA_OPTS for reliable Gradle heap tuning.

Gradle may not honor JAVA_OPTS in all launch contexts. Set GRADLE_OPTS or pass org.gradle.jvmargs.

       - name: Build example for Android
         env:
-          JAVA_OPTS: '-XX:MaxHeapSize=6g'
+          JAVA_OPTS: '-XX:MaxHeapSize=6g'
+          GRADLE_OPTS: '-Xmx6g -Dorg.gradle.jvmargs=-Xmx6g'
           CI: 1

135-139: Same redundancy here; drop per-step NODE_OPTIONS.

Top-level env already covers this.

-      - name: Install example dependencies
-        run: cd example && yarn install --immutable
-        env:
-          NODE_OPTIONS: --max-old-space-size=4096
+      - name: Install example dependencies
+        run: cd example && yarn install --immutable

163-163: Gating CocoaPods on turbo cache hit is clever; add a safety check.

If a cache is mistakenly HIT on a cold workspace, Pods might be missing. Consider also ensuring Pods/Manifest.lock exists before skipping.

-      - name: Install cocoapods
-        if: env.turbo_cache_hit != 1
+      - name: Install cocoapods
+        if: env.turbo_cache_hit != 1
         run: |
           cd example
+          if [ ! -f "ios/Pods/Manifest.lock" ]; then export FORCE_POD_INSTALL=1; fi
           if [ -f "Gemfile" ]; then
             bundle install
-            bundle exec pod repo update --verbose
-            bundle exec pod install --project-directory=ios
+            bundle exec pod install --project-directory=ios ${FORCE_POD_INSTALL:+--repo-update}
           else
-            pod install --project-directory=ios
+            pod install --project-directory=ios ${FORCE_POD_INSTALL:+--repo-update}
           fi

166-172: Speed up CocoaPods: avoid unconditional repo updates and cache Bundler.

Use --repo-update only when needed (as above) and add Bundler caching via ruby/setup-ruby for faster builds.

I can draft a small step using ruby/setup-ruby@v1 with bundler-cache: true right before this step if you want it included here.


175-176: Redundant CI env at step-level.

Already set at the job level; safe to remove.

-      - name: Build example for iOS
-        env:
-          CI: 1
+      - name: Build example for iOS
         run: |
           yarn turbo run build:ios --cache-dir="${{ env.TURBO_CACHE_DIR }}"
📜 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.

📥 Commits

Reviewing files that changed from the base of the PR and between 34a7f3d and f25f7b7.

⛔ Files ignored due to path filters (1)
  • yarn.lock is excluded by !**/yarn.lock, !**/*.lock
📒 Files selected for processing (3)
  • .github/workflows/ci.yml (5 hunks)
  • example/package.json (1 hunks)
  • package.json (3 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • package.json
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: build-android
🔇 Additional comments (6)
example/package.json (3)

8-9: Switch to expo run for platform builds looks right.

These scripts align with the native build flow introduced by Expo SDK 52.


20-21: Expo SDK 52 patch bumps look good.

expo-image-picker ~16.0.3 and expo-status-bar ~2.0.1 match the SDK 52 range.


23-23: Double-check React Native version alignment with Expo 52.

Ensure 0.76.9 matches what Expo 52 expects in this repo configuration; if uncertain, run “expo install react-native” to sync.

.github/workflows/ci.yml (3)

63-63: Setting CI=1 at the job level is fine.

Many tools key off this flag; having it explicit per job is OK.


127-127: CI=1 on iOS job is fine.

Consistent with Android.


13-17: CI Setup Verified – Corepack Enablement and Yarn Pinning Present

The composite action at .github/actions/setup/action.yml includes a dedicated Enable Corepack step running corepack enable, and the root package.json has "packageManager": "yarn@3.6.1", ensuring the Yarn version is pinned and preventing classic-vs-Berry drift. No additional changes are required here.

@anngth
anngth merged commit b3e4d64 into main Aug 28, 2025
6 checks passed
@anngth
anngth deleted the fix_error branch August 28, 2025 06:14
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant