@@ -72,21 +72,15 @@ class CameraViewController: UIViewController, UIDocumentPickerDelegate, UINaviga
7272 override func viewDidAppear( _ animated: Bool ) {
7373 super. viewDidAppear ( animated)
7474
75- DispatchQueue . global ( qos: . userInitiated) . async { [ weak self] in
76- self ? . captureSession. startRunning ( )
77- }
78-
79- // Reconnect WebSocket if not connected
80- if !isConnected && !isAttemptingConnection {
81- setupWebSocketConnection ( )
82- }
75+ // Check and request permissions each time the view appears
76+ checkAndRequestPermissions ( )
8377 }
8478
8579 override func viewWillDisappear( _ animated: Bool ) {
8680 super. viewWillDisappear ( animated)
8781
88- if captureSession. isRunning {
89- captureSession . stopRunning ( )
82+ if let session = captureSession, session . isRunning {
83+ session . stopRunning ( )
9084 }
9185
9286 // Close WebSocket connection
@@ -515,67 +509,143 @@ class CameraViewController: UIViewController, UIDocumentPickerDelegate, UINaviga
515509 }
516510
517511 private func setupCamera( ) {
518- captureSession = AVCaptureSession ( )
519- captureSession. sessionPreset = . high
512+ // Camera initialization will be handled in checkAndRequestPermissions
513+ setupFaceDetection ( )
514+ }
520515
521- guard let videoCaptureDevice = AVCaptureDevice . default ( . builtInWideAngleCamera, for: . video, position: currentCameraPosition) else {
522- print ( " Failed to get camera device " )
516+ private func setupAudioInput( ) {
517+ guard let audioDevice = AVCaptureDevice . default ( for: . audio) else {
518+ print ( " Failed to get audio device " )
523519 return
524520 }
525521
526- guard let videoInput = try ? AVCaptureDeviceInput ( device: videoCaptureDevice ) else {
527- print ( " Failed to create video input " )
522+ guard let audioInput = try ? AVCaptureDeviceInput ( device: audioDevice ) else {
523+ print ( " Failed to create audio input " )
528524 return
529525 }
530526
531- if captureSession. canAddInput ( videoInput ) {
532- captureSession. addInput ( videoInput )
527+ if captureSession. canAddInput ( audioInput ) {
528+ captureSession. addInput ( audioInput )
533529 }
534530
535- videoOutput = AVCaptureVideoDataOutput ( )
536- videoOutput. setSampleBufferDelegate ( self , queue: DispatchQueue ( label: " videoQueue " ) )
537- videoOutput. videoSettings = [ kCVPixelBufferPixelFormatTypeKey as String : kCVPixelFormatType_32BGRA]
531+ audioOutput = AVCaptureAudioDataOutput ( )
532+ audioOutput. setSampleBufferDelegate ( self , queue: DispatchQueue ( label: " audioQueue " ) )
538533
539- if captureSession. canAddOutput ( videoOutput ) {
540- captureSession. addOutput ( videoOutput )
534+ if captureSession. canAddOutput ( audioOutput ) {
535+ captureSession. addOutput ( audioOutput )
541536 }
537+ }
542538
543- // Request microphone access and setup audio
539+ private func checkAndRequestPermissions( ) {
540+ let cameraStatus = AVCaptureDevice . authorizationStatus ( for: . video)
541+
542+ switch cameraStatus {
543+ case . authorized:
544+ // Camera permission already granted
545+ initializeCameraIfNeeded ( )
546+ requestMicrophonePermission ( )
547+ case . denied, . restricted:
548+ // Permission was denied or restricted
549+ showPermissionDeniedAlert ( permissionType: " Camera " )
550+ case . notDetermined:
551+ // Request permission
552+ AVCaptureDevice . requestAccess ( for: . video) { [ weak self] granted in
553+ DispatchQueue . main. async {
554+ if granted {
555+ self ? . initializeCameraIfNeeded ( )
556+ self ? . requestMicrophonePermission ( )
557+ } else {
558+ self ? . showPermissionDeniedAlert ( permissionType: " Camera " )
559+ }
560+ }
561+ }
562+ @unknown default :
563+ print ( " Unknown camera permission status " )
564+ }
565+ }
566+
567+ private func requestMicrophonePermission( ) {
544568 AVAudioApplication . requestRecordPermission { [ weak self] granted in
545569 if granted {
546570 DispatchQueue . main. async {
547571 self ? . setupAudioInput ( )
548572 }
549573 } else {
550574 print ( " Microphone permission denied " )
575+ DispatchQueue . main. async {
576+ self ? . showPermissionDeniedAlert ( permissionType: " Microphone " )
577+ }
551578 }
552579 }
553-
554- // Set initial video orientation
555- updateVideoOrientation ( )
556580 }
557581
558- private func setupAudioInput( ) {
559- guard let audioDevice = AVCaptureDevice . default ( for: . audio) else {
560- print ( " Failed to get audio device " )
582+ private func initializeCameraIfNeeded( ) {
583+ guard captureSession == nil else {
584+ // Camera already initialized, just start running
585+ if !captureSession. isRunning {
586+ DispatchQueue . global ( qos: . userInitiated) . async { [ weak self] in
587+ self ? . captureSession? . startRunning ( )
588+ }
589+ }
590+ // Reconnect WebSocket if not connected
591+ if !isConnected && !isAttemptingConnection {
592+ setupWebSocketConnection ( )
593+ }
561594 return
562595 }
563596
564- guard let audioInput = try ? AVCaptureDeviceInput ( device: audioDevice) else {
565- print ( " Failed to create audio input " )
597+ captureSession = AVCaptureSession ( )
598+ captureSession. sessionPreset = . high
599+
600+ // Load saved camera position preference
601+ let savedCameraPosition = UserDefaults . standard. string ( forKey: " cameraPosition " ) ?? " front "
602+ currentCameraPosition = savedCameraPosition == " back " ? . back : . front
603+
604+ guard let videoCaptureDevice = AVCaptureDevice . default ( . builtInWideAngleCamera, for: . video, position: currentCameraPosition) else {
605+ print ( " Failed to get camera device " )
566606 return
567607 }
568608
569- if captureSession. canAddInput ( audioInput) {
570- captureSession. addInput ( audioInput)
609+ guard let videoInput = try ? AVCaptureDeviceInput ( device: videoCaptureDevice) else {
610+ print ( " Failed to create video input " )
611+ return
571612 }
572613
573- audioOutput = AVCaptureAudioDataOutput ( )
574- audioOutput. setSampleBufferDelegate ( self , queue: DispatchQueue ( label: " audioQueue " ) )
614+ if captureSession. canAddInput ( videoInput) {
615+ captureSession. addInput ( videoInput)
616+ }
575617
576- if captureSession. canAddOutput ( audioOutput) {
577- captureSession. addOutput ( audioOutput)
618+ videoOutput = AVCaptureVideoDataOutput ( )
619+ videoOutput. setSampleBufferDelegate ( self , queue: DispatchQueue ( label: " videoQueue " ) )
620+ videoOutput. videoSettings = [ kCVPixelBufferPixelFormatTypeKey as String : kCVPixelFormatType_32BGRA]
621+
622+ if captureSession. canAddOutput ( videoOutput) {
623+ captureSession. addOutput ( videoOutput)
578624 }
625+
626+ // Set initial video orientation
627+ updateVideoOrientation ( )
628+
629+ // Start running
630+ DispatchQueue . global ( qos: . userInitiated) . async { [ weak self] in
631+ self ? . captureSession? . startRunning ( )
632+ }
633+
634+ // Reconnect WebSocket if not connected
635+ if !isConnected && !isAttemptingConnection {
636+ setupWebSocketConnection ( )
637+ }
638+ }
639+
640+ private func showPermissionDeniedAlert( permissionType: String ) {
641+ let alert = UIAlertController ( title: " \( permissionType) Permission Required " , message: " This app needs \( permissionType. lowercased ( ) ) access to function properly. Please enable it in Settings. " , preferredStyle: . alert)
642+ alert. addAction ( UIAlertAction ( title: " Go to Settings " , style: . default) { _ in
643+ if let url = URL ( string: UIApplication . openSettingsURLString) {
644+ UIApplication . shared. open ( url)
645+ }
646+ } )
647+ alert. addAction ( UIAlertAction ( title: " Cancel " , style: . cancel) )
648+ self . present ( alert, animated: true )
579649 }
580650
581651 private func updateVideoOrientation( ) {
0 commit comments