0

I'm trying to open the camera with AVKit and while creating CameraView and AVCaptureVideoPreviewLayer object I have the following error:
Publishing changes from within view updates is not allowed, this will cause undefined behavior.

I know that it could be resolved with adding DispatchQueue.main.async {} but I do not know exactly where to put it.

I'm creating CameraView like:

CameraView(size: geometry.size)
                            .environmentObject(cameraViewModel)
                            .ignoresSafeArea()

Inside, it look like:

struct CameraView: UIViewRepresentable {
    @EnvironmentObject var cameraViewModel: CameraViewModel
    
    var size: CGSize
    
    func makeUIView(context: Context) -> UIView {
        let view = UIView()
        
        /* error occurs here */
        cameraViewModel.capturePreview = AVCaptureVideoPreviewLayer(session: cameraViewModel.captureSession)

        cameraViewModel.capturePreview.frame.size = size
        
        cameraViewModel.capturePreview.videoGravity = .resizeAspectFill
        
        view.layer.addSublayer(cameraViewModel.capturePreview)
        
        return view
    }
    
    func updateUIView(_ uiView: UIView, context: Context) {
        
    }
}

And lastly, CameraViewModel holds capturePreview which is:

@Published var capturePreview: AVCaptureVideoPreviewLayer!
Vader20FF
  • 271
  • 2
  • 9
  • Probably best to pass the session and not the layer. But there is no way to help you specifically since we don’t know when/how the changes are being made or the line that is throwing the error. – lorem ipsum Sep 14 '22 at 11:34
  • @loremipsum I made a comment in code above that identifies the line where the error occurs – Vader20FF Sep 14 '22 at 11:55
  • sorry yeah I missed that, the same applies. I wouldn’t hold on the view stuff in the model. SwiftUI views have very specific lifecycle events. When using one of the representables there are always a ton of issues if you make changes to the views or controllers outside of make and update. – lorem ipsum Sep 14 '22 at 12:08
  • @loremipsum Where would you configure capturePreview then? In ViewModel? – Vader20FF Sep 14 '22 at 12:49
  • arguments/variables for the `UIViewRepresentable`, more specifically `let variables`, if I was going to change the layer while capturing/after its creation I would create a `UIViewController` and interface with it directly. – lorem ipsum Sep 14 '22 at 13:05
  • @Vader20FF did you figure out where/how to configure capturePreview properly here? i'm having the same issue with similar code... getting that same warning and launching the camera view for the first time takes forever. – mapes911 Oct 08 '22 at 18:56
  • @mapes911, unfortunately I have been away for the recent time and did not have the opportunity to implement the solution proposed by loremipsum. As soon as I have some time to do it I will post the solution or comment here – Vader20FF Oct 10 '22 at 13:28
  • @mapes911 I managed to solve it. I have posted an answer here. – Vader20FF Nov 17 '22 at 10:51

1 Answers1

2

I managed to solve the problem by removing @Published var capturePreview: AVCaptureVideoPreviewLayer! from cameraViewModel and restructuring my CameraView like this:

struct CameraView: UIViewRepresentable {
    @EnvironmentObject var cameraViewModel: CameraViewModel
    
    var size: CGSize
    
    func makeUIView(context: Context) -> UIView {
        let view = UIView()
        view.layer.addSublayer(setupCapturePreview())
        return view
    }
    
    func updateUIView(_ uiView: UIView, context: Context) {
        
    }
    
    private func setupCapturePreview() -> AVCaptureVideoPreviewLayer {
        let capturePreview = AVCaptureVideoPreviewLayer(session: cameraViewModel.captureSession)
        capturePreview.frame.size = size
        capturePreview.videoGravity = .resizeAspectFill
        return capturePreview
    }
}
Vader20FF
  • 271
  • 2
  • 9