# tauri-plugin-barcode-scanner-continuous
Fork of [`@tauri-apps/plugin-barcode-scanner`](https://github.com/tauri-apps/plugins-workspace/tree/v2/plugins/barcode-scanner) with on-device fixes that enable a **stable continuous scan loop** on iOS and Android.
The API surface is the same as the upstream plugin (`scan`, `cancel`, `checkPermissions`, `requestPermissions`, `openAppSettings`), so it is a near drop-in replacement when the consumer wants to keep a scanner overlay open across many detections — a common pattern in Point-of-Sale checkout flows.
| iOS | ✓ |
| Android | ✓ |
| macOS / Windows / Linux | — |
## Why this fork
`tauri-plugin-barcode-scanner 2.4.4` (the latest release on crates.io at the time of this fork) had two unresolved on-device bugs that prevented a reliable continuous scanning UX:
1. **iOS app crash when `cancel()` is called while a scan is live.** Upstream's `cancel()` runs on the IPC thread, so tearing the capture session down from JS races `destroy()` on the main thread. See [tauri-apps/plugins-workspace#3081](https://github.com/tauri-apps/plugins-workspace/issues/3081).
2. **iOS taps stop working after a scan.** `dismantleCamera()` removed the preview layer but left the `UIView` in the superview hierarchy, intercepting touches. See [PR #3107](https://github.com/tauri-apps/plugins-workspace/pull/3107) / [PR #2440](https://github.com/tauri-apps/plugins-workspace/pull/2440).
Both are fixed in unreleased upstream work, but as of `2.4.4` they were not in any published crate.
This fork carries those patches plus a small additive feature so it can live alongside the upstream plugin in a project without conflicting with its ACL / Tauri plugin namespace.
## What is different from upstream
### iOS (Swift)
- `cancel()` wraps its body in `DispatchQueue.main.async { … }`. Fixes the crash when the close button is pressed while `scan()` is still running.
- `dismantleCamera()` now calls `self.cameraView.removeFromSuperview()`. Fixes the unresponsive-after-scan bug.
- `scan()` accepts an optional `overlayInsetBottom` argument (points). When running in non-windowed mode on iOS, this leaves a transparent strip at the bottom of the camera preview so apps can keep their own UI (e.g. a cart bottom-sheet) visible.
### Renamed identifiers (to avoid collision with upstream)
| Crate name | `tauri-plugin-barcode-scanner` | `tauri-plugin-barcode-scanner-continuous` |
| Rust lib | `tauri_plugin_barcode_scanner` | `tauri_plugin_barcode_scanner_continuous` |
| Tauri plugin name | `barcode-scanner` | `barcode-scanner-continuous` |
| ACL prefix | `barcode-scanner:*` | `barcode-scanner-continuous:*` |
| iOS `@_cdecl` init | `init_plugin_barcode_scanner` | `init_plugin_barcode_scanner_continuous` |
| Android Java package | `app.tauri.barcodescanner` | `app.tauri.barcodescannercontinuous` |
Installing both this fork and the upstream plugin in the same app is supported.
## Install
### Rust (`src-tauri/Cargo.toml`)
```toml
[target.'cfg(any(target_os = "android", target_os = "ios"))'.dependencies]
tauri-plugin-barcode-scanner-continuous = "0.1"
```
### JavaScript
```bash
npm install @riczescaran/tauri-plugin-barcode-scanner-continuous
```
### Register the plugin (`src-tauri/src/lib.rs`)
```rust
#[cfg(any(target_os = "android", target_os = "ios"))]
let builder = builder.plugin(tauri_plugin_barcode_scanner_continuous::init());
```
### Allow the commands (`src-tauri/capabilities/mobile.json`)
```json
{
"permissions": ["barcode-scanner-continuous:default"]
}
```
## Usage
```ts
import {
scan,
cancel,
Format,
} from '@riczescaran/tauri-plugin-barcode-scanner-continuous'
// Continuous loop — the caller drives the loop; the plugin resolves one
// scan() call per detection and internally tears down the capture session
// in a way that is safe to re-arm on the next iteration.
async function runScanLoop() {
while (shouldKeepScanning()) {
try {
const { content } = await scan({
windowed: true,
formats: [Format.EAN13, Format.EAN8, Format.QRCode],
})
addToCart(content)
} catch (e) {
// cancel() rejects the in-flight scan() with "cancelled" — this is
// the normal exit from the loop.
if (String(e).match(/cancel/i)) break
throw e
}
}
}
// Close the overlay:
await cancel()
```
## License
Apache-2.0 OR MIT. Derivative work of the upstream `@tauri-apps/plugin-barcode-scanner`, Copyright © 2019-Present — The Tauri Programme within The Commons Conservancy.
See `LICENSE_APACHE-2.0` and `LICENSE_MIT`.