tauri-apps / tauri-apps/plugins-workspace
[barcode-scanner] iOS: scan() promise never resolves in windowed mode
- Dominant language
- Rust
- Stars
- 1.8k
- Forks
- 602
- Avg merge
- 4d 14h
- Merged PRs (30d)
- 9
Description
### Describe the bug
When using the barcode scanner plugin on iOS with `windowed: true`, the JavaScript promise returned by `scan()` never resolves — even though the native side successfully detects the barcode and sends the IPC response.
With `windowed: false`, the same code works perfectly.
### Reproduction
```typescript
import { scan, Format } from '@tauri-apps/plugin-barcode-scanner';
// This never resolves on iOS:
const result = await scan({ windowed: true, formats: [Format.QRCode] });
console.log(result); // Never reached
```
### Evidence
- The IPC response IS visible in Safari's network inspector — the native side returns `{"content": "D56UPN", "format": "QR_CODE"}` correctly
- But the JavaScript `await scan()` never continues — no code after `await` executes
- `windowed: false` works correctly with identical JavaScript code
- `cancel()` works correctly (the reject path is unaffected)
### Analysis
Looking at `BarcodeScannerPlugin.swift`, the `metadataOutput` delegate calls:
```swift
invoke?.resolve(jsObject)
destroy()
```
`destroy()` runs synchronously right after `resolve()` on the same main queue and does:
```swift
dismantleCamera() // stops AVCaptureSession (can block main thread)
invoke = nil
if windowed {
webView.isOpaque = true // restores webview
webView.backgroundColor = backgroundColor
webView.scrollView.backgroundColor = backgroundColor
}
```
In non-windowed mode, the `if windowed` block is skipped and `resolve()` works fine. In windowed mode, the webview property restoration (`isOpaque`, `backgroundColor`) appears to interfere with the pending IPC callback delivery before it reaches the JavaScript layer.
### Potential fix
Reorder the operations in `metadataOutput` so that `resolve()` is called **after** all cleanup and webview restoration, not before:
```swift
self.isScanning = false
dismantleCamera()
if windowed {
let backgroundColor = previousBackgroundColor ?? UIColor.white
webView.isOpaque = true
webView.backgroundColor = backgroundColor
webView.scrollView.backgroundColor = backgroundColor
}
invoke?.resolve(jsObject)
invoke = nil
```
This way the webview is fully restored to its normal (opaque) state before the IPC response is sent — matching the state where `resolve()` is known to work (non-windowed mode).
I've tested this fix locally and it resolves the issue. Happy to submit a PR if this approach looks correct.
### Expected behavior
`scan({ windowed: true })` should resolve with the scanned barcode content, same as `windowed: false`.
### Platform and versions
- iOS 18.7 (iPhone)
- Tauri v2
- `@tauri-apps/plugin-barcode-scanner` v2.4.4
- `tauri-plugin-barcode-scanner` from `v2` branch
### Stack trace
_No crash — the promise simply never settles._
Contributor guide
Assessment
This issue has not been assessed yet.