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
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:
invoke?.resolve(jsObject)
destroy()
destroy() runs synchronously right after resolve() on the same main queue and does:
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:
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.
Describe the bug
When using the barcode scanner plugin on iOS with
windowed: true, the JavaScript promise returned byscan()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
Evidence
{"content": "D56UPN", "format": "QR_CODE"}correctlyawait scan()never continues — no code afterawaitexecuteswindowed: falseworks correctly with identical JavaScript codecancel()works correctly (the reject path is unaffected)Analysis
Looking at
BarcodeScannerPlugin.swift, themetadataOutputdelegate calls:destroy()runs synchronously right afterresolve()on the same main queue and does:In non-windowed mode, the
if windowedblock is skipped andresolve()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
metadataOutputso thatresolve()is called after all cleanup and webview restoration, not before: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 aswindowed: false.Platform and versions
@tauri-apps/plugin-barcode-scannerv2.4.4tauri-plugin-barcode-scannerfromv2branchStack trace
No crash — the promise simply never settles.