[BUG]Crash at second time to present camera view
Expected Behavior
Show camera view, close the view, re-open the view.
Current Behavior
Show camera view, close the view, re-open the view but crash.
cameraView.layer.addSublayer(cameraLayer) Thread 1: Fatal error: Unexpectedly found nil while implicitly unwrapping an Optional value
Code Sample
Just the most simple way to create a MCamera() and .startSession()
Screenshots
Context
Please provide any relevant information about your setup. This is important in case the issue is not reproducible except for under certain conditions.
| Name | Version |
|---|---|
| SDK | e.g. 3.0.1 |
| Xcode | e.g. 16.1 |
| Operating System | e.g. iOS 18.2 |
| Device | e.g. iPhone 13 Pro Max |
I'm experiencing exactly the same issue.
Usage
struct ImageCaptureView: View {
@Environment(\.dismiss) private var dismiss
var body: some View {
MCamera()
.onImageCaptured { image, controller in
controller.closeMCamera()
}
.setAudioAvailability(false)
.setCloseMCameraAction {
dismiss()
}
.startSession()
}
}
This view is presented using .fullScreenCover(...).
As described by @ljhao24, the camera view works fine when presented for the first time, but crashes as follows when presented a second time:
MijickCamera/CameraManager.swift:77: Fatal error: Unexpectedly found nil while implicitly unwrapping an Optional value
The camera manager's cameraView property is nil.
Context:
- SDK 3.0.1
- Xcode 16.2
- Operating System: iOS 18.3.1
- Device: iPhone 15 Pro
I have invested some time investigating the cause of the crash.
The problem here is that MCamera has a default initializer that creates a new CameraManager instance, along with a new AVCaptureSession for every MCamera instance that is created. This is a problem when using MCamera in a view whose body is evaluated more than once.
I was able to reproduce the crash in an a minimal example app:
- Embed the
MCamerain a wrapper view, calledCameraView - Add an
@Environmentproperty toCameraViewspecifying a closure that is called when an image is captured
import MijickCamera
import SwiftUI
struct CameraView: View {
@Environment(\.dismiss) private var dismiss
@Environment(\.imageCaptureAction) private var capture
var body: some View {
MCamera()
.onImageCaptured { image, controller in
guard let data = image.jpegData(compressionQuality: 0.8) else { return }
capture(data)
controller.closeMCamera()
}
.setCloseMCameraAction {
dismiss()
}
.startSession()
}
}
extension EnvironmentValues {
@Entry var imageCaptureAction: (Data) -> Void = { _ in }
}
This view is then presented full screen from the app's ContentView:
struct ContentView: View {
@State private var isCameraPresented: Bool = false
var body: some View {
Button {
isCameraPresented = true
} label: {
Image(systemName: "camera.fill")
.font(.largeTitle)
.imageScale(.large)
}
.fullScreenCover(isPresented: $isCameraPresented) {
CameraView()
}
}
}
CAUSE
The CaptureView body is evaluated multiple times because of the @Environment(\.imageCaptureAction) property. For reasons I am not aware of the addition of this environment property causes the body to be evaluated twice rather than once. This is enough to cause the app to crash when the CameraView is displayed a second time.
The root issue here is the MCamera initializer that, as mentioned above, creates a new CameraManager for every Camera struct instantiated, i.e. every time the containing view's body is evaluated.
WORKAROUND
The only workaround I see for this issue is to ensure that MCamera is only instantiated once for each presentation. This precludes wrapping MCamera in another view: instead use MCamera directly within the fullScreenCover(...) closure that is presenting it.
I discovered that If it's wrapped in a NavigationStack it won't crash, but it won't stop the session upon dismiss.
I patched the ObservableObject that holds the CameraManager and converted it to StateObject, and since then I haven't seen any crashes.