SwiftUI Camera Preview – Mirror App

import SwiftUI
import UIKit
import AVFoundation

class CameraModel: NSObject, ObservableObject, AVCapturePhotoCaptureDelegate {
    
    enum CameraDirection {
        case front
        case back
    }
    
    @Published var session:AVCaptureSession!
    @Published var cameraDirection = AVCaptureDevice.Position.front
    @Published var input: AVCaptureDeviceInput?
    @Published var output = AVCapturePhotoOutput()
    
    @Published var preview: AVCaptureVideoPreviewLayer!

    override init() {
        let lsession = AVCaptureSession()
        preview = AVCaptureVideoPreviewLayer(session: lsession)
        session = lsession
    }
    
    func Check() {
        switch AVCaptureDevice.authorizationStatus(for: .video) {
        case .authorized:
            print("authorized")
            setup()
        case .notDetermined:
            print("not determined")
            AVCaptureDevice.requestAccess(for: .video) { status in
                if status {
                    self.setup()
                }
            }
        default:
            print("unknown")
        }
    }
    
    let discoverySession = AVCaptureDevice.DiscoverySession(deviceTypes:
        [.builtInTrueDepthCamera, .builtInDualCamera, .builtInWideAngleCamera],
        mediaType: .video, position: .unspecified)
    
    func bestDevice(in position: AVCaptureDevice.Position) -> AVCaptureDevice {
        let devices = self.discoverySession.devices
        guard !devices.isEmpty else { fatalError("Missing capture devices.")}

        return devices.first(where: { device in device.position == position })!
    }
    
    
    private func getDevice(direction: AVCaptureDevice.Position) -> AVCaptureDevice? {
        if let device = AVCaptureDevice.default(.builtInDualCamera, for: .video, position: direction) {
            return device
        }
        
        // Find the built-in Wide-Angle Camera, if it exists.
        if let device = AVCaptureDevice.default(.builtInWideAngleCamera, for: .video, position: direction) {
            return device
        }
        
        fatalError("Missing expected back camera device.")
        //throw Error("No camera device")
    }
    
    func setup() {
        do {
            self.session.beginConfiguration()
            
            let device =  bestDevice(in: cameraDirection)
            input  = try AVCaptureDeviceInput(device: device)
            
            if input != nil && self.session.canAddInput(input!) {
                self.session.addInput(input!)
            }
            
            if self.session.canAddOutput(output) {
                self.session.addOutput(output)
            }
            
            self.session.commitConfiguration()
        }
        catch {
            print(error.localizedDescription)
        }
        
    }
}


struct CameraView: UIViewRepresentable {
    @ObservedObject var camera: CameraModel
    
    func makeUIView(context: Context) -> some UIView {
        let view = UIView(frame: UIScreen.main.bounds)
        
        camera.preview.frame = view.frame
        camera.preview.videoGravity = .resizeAspectFill
        view.layer.addSublayer(camera.preview)
        
        DispatchQueue.global(qos: .background).async {
            camera.session.startRunning()
        }
        return view
    }
    
    func updateUIView(_ uiView: UIViewType, context: Context) {
        
    }
    
}

struct MirrorView: View {
    @StateObject var camera = CameraModel()
    var body: some View {
        ZStack {
            CameraView(camera: camera)
                .frame(maxWidth: .infinity, maxHeight: .infinity)
                .edgesIgnoringSafeArea(.all)
        }
        .onAppear {
            camera.Check()
        }
    }
}



struct MirrorView_Previews: PreviewProvider {
    static var previews: some View {
        MirrorView()
    }
}