Skip to content

Conversation

@EthanDM
Copy link

@EthanDM EthanDM commented Oct 18, 2025

What

Fix the navigation freeze on React Navigation/Fabric by shutting the camera session down deterministically when the view unmounts.

Changes

  • Add CameraSession.shutdown, which disables photo/video/audio/code-scanner/location/torch before flipping isActive = false, and clears delegates while removing outputs.
  • Stop both capture sessions asynchronously during CameraSession.deinit as a backstop.
  • Have CameraView call the shutdown helper on unmount/deinit, log if teardown exceeds 2 s, and guard against concurrent shutdowns.

Tested on

  • iPhone 14 Pro (iOS 26.0.1) with code scanner enabled, navigating away from the camera screen (no freeze).

Related issues

- Add shutdown method to stop all outputs and release resources
- Schedule shutdown with warning if it takes longer than expected
@vercel
Copy link

vercel bot commented Oct 18, 2025

@EthanDM is attempting to deploy a commit to the mrousavy's Team Team on Vercel.

A member of the Team first needs to authorize it.

@jkaufman
Copy link
Contributor

jkaufman commented Nov 8, 2025

@EthanDM Thank you for your effort on this. We’ve been testing a patch that builds on yours and would appreciate a look.

Copy link
Owner

@mrousavy mrousavy left a comment

Choose a reason for hiding this comment

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

Thanks for this PR - seems like there were some issues with proper session shutdown on Fabric - I wonder why that wasn't a problem before on Paper. Maybe view caching/recycling?

Anyways - I left some review comments

try lambda(config)
} catch CameraConfiguration.AbortThrow.abort {
// call has been aborted and changes shall be discarded
completionBlock?()
Copy link
Owner

Choose a reason for hiding this comment

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

instead of writing that 3 times, put it in a defer { ... } block so we dont forget it when we add a fourth exit point

Comment on lines +145 to +147
deinit {
shutdownCameraSession()
}
Copy link
Owner

Choose a reason for hiding this comment

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

Putting stuff like this in deinit is often a bad idea, especially if it runs an asynchronous operation.
Can you please test if that is absolutely necessary? Remove it if it isn't - we are calling shutdownCameraSession() at the top anyways.

Comment on lines +305 to +311
let slowShutdownWarning = DispatchWorkItem {
VisionLogger.log(level: .warning, message: "CameraSession shutdown is still running after 2 seconds.")
}
CameraQueues.cameraQueue.asyncAfter(deadline: .now() + .seconds(2), execute: slowShutdownWarning)

cameraSession.shutdown { [weak self] in
slowShutdownWarning.cancel()
Copy link
Owner

Choose a reason for hiding this comment

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

Suggested change
let slowShutdownWarning = DispatchWorkItem {
VisionLogger.log(level: .warning, message: "CameraSession shutdown is still running after 2 seconds.")
}
CameraQueues.cameraQueue.asyncAfter(deadline: .now() + .seconds(2), execute: slowShutdownWarning)
cameraSession.shutdown { [weak self] in
slowShutdownWarning.cancel()
let logSlowShutdownWarning = DispatchWorkItem {
VisionLogger.log(level: .warning, message: "CameraSession shutdown is still running after 2 seconds.")
}
CameraQueues.cameraQueue.asyncAfter(deadline: .now() + .seconds(2), execute: logSlowShutdownWarning)
cameraSession.shutdown { [weak self] in
logSlowShutdownWarning.cancel()

Comment on lines +64 to +69
if let metadataOutput = output as? AVCaptureMetadataOutput {
metadataOutput.setMetadataObjectsDelegate(nil, queue: nil)
}
if let videoOutput = output as? AVCaptureVideoDataOutput {
videoOutput.setSampleBufferDelegate(nil, queue: nil)
}
Copy link
Owner

Choose a reason for hiding this comment

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

I'm 99% sure this is unnecessary. The outputs store the delegates as weak references, and there's no Thread hops involved.

Can you try to remove this code and check if it still works fine for you? I'd rather have that out tbh because it is super specific and feels like monkeypatching

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.

🐛 Navigating away on React Native 0.78+, XCode 26, and React Native Vision Camera v4.7.1 freezes the app

3 participants