From 55b48575cc20e55f85ef1fe2f10ef71f4b10ec76 Mon Sep 17 00:00:00 2001 From: pulipakaa24 Date: Sun, 15 Feb 2026 14:15:34 -0600 Subject: [PATCH] Camera works, provided you don't go back too rapidly --- SousChefAI/Services/CameraManager.swift | 38 ++++++++++++++++++- .../ViewModels/CookingModeViewModel.swift | 2 +- SousChefAI/ViewModels/ScannerViewModel.swift | 9 ++++- SousChefAI/Views/CookingModeView.swift | 6 ++- SousChefAI/Views/ScannerView.swift | 36 ++++++++++++++++-- 5 files changed, 80 insertions(+), 11 deletions(-) diff --git a/SousChefAI/Services/CameraManager.swift b/SousChefAI/Services/CameraManager.swift index 5c18c98..88eec15 100644 --- a/SousChefAI/Services/CameraManager.swift +++ b/SousChefAI/Services/CameraManager.swift @@ -26,9 +26,11 @@ final class CameraManager: NSObject, ObservableObject { private let continuationQueue = DispatchQueue(label: "com.souschef.continuation") private var isConfigured = false + private var cachedPreviewLayer: AVCaptureVideoPreviewLayer? nonisolated override init() { super.init() + print("🎥 CameraManager.init() - Instance created at \(Date())") } // MARK: - Authorization @@ -53,8 +55,12 @@ final class CameraManager: NSObject, ObservableObject { // MARK: - Session Setup func setupSession() async throws { + print("🎥 CameraManager.setupSession() - STARTED at \(Date())") // Only configure once - guard !isConfigured else { return } + guard !isConfigured else { + print("🎥 CameraManager.setupSession() - Already configured, returning") + return + } // Ensure authorization is checked first await checkAuthorization() @@ -63,19 +69,29 @@ final class CameraManager: NSObject, ObservableObject { throw CameraError.notAuthorized } + print("🎥 CameraManager.setupSession() - Calling beginConfiguration()") captureSession.beginConfiguration() // Set session preset captureSession.sessionPreset = .high + print("🎥 CameraManager.setupSession() - Set preset to .high") // Add video input + print("🎥 CameraManager.setupSession() - About to get video device (LINE 72)") + + #if targetEnvironment(simulator) + print("🎥 CameraManager.setupSession() - ⚠️ RUNNING ON SIMULATOR - Camera may not work properly") + #endif + guard let videoDevice = AVCaptureDevice.default(.builtInWideAngleCamera, for: .video, position: .back), let videoInput = try? AVCaptureDeviceInput(device: videoDevice), captureSession.canAddInput(videoInput) else { + print("🎥 CameraManager.setupSession() - ❌ FAILED to get video device or add input") captureSession.commitConfiguration() throw CameraError.setupFailed } + print("🎥 CameraManager.setupSession() - ✅ Successfully got video device and input") captureSession.addInput(videoInput) // Add video output @@ -96,6 +112,7 @@ final class CameraManager: NSObject, ObservableObject { captureSession.commitConfiguration() isConfigured = true + print("🎥 CameraManager.setupSession() - ✅ COMPLETED successfully at \(Date())") } // MARK: - Session Control @@ -151,9 +168,26 @@ final class CameraManager: NSObject, ObservableObject { // MARK: - Preview Layer - func previewLayer() -> AVCaptureVideoPreviewLayer { + func previewLayer() -> AVCaptureVideoPreviewLayer? { + print("🎥 CameraManager.previewLayer() - ⚠️ CALLED at \(Date()) - isConfigured: \(isConfigured)") + + // Only create preview layer after session is configured + if !isConfigured { + print("🎥 CameraManager.previewLayer() - ❌ Session not configured yet, returning nil") + return nil + } + + // Return cached layer if available + if let cached = cachedPreviewLayer { + print("🎥 CameraManager.previewLayer() - ✅ Returning cached preview layer") + return cached + } + + // Create and cache new preview layer + print("🎥 CameraManager.previewLayer() - ✅ Creating new preview layer") let layer = AVCaptureVideoPreviewLayer(session: captureSession) layer.videoGravity = .resizeAspectFill + cachedPreviewLayer = layer return layer } } diff --git a/SousChefAI/ViewModels/CookingModeViewModel.swift b/SousChefAI/ViewModels/CookingModeViewModel.swift index c926f1d..cc25e76 100644 --- a/SousChefAI/ViewModels/CookingModeViewModel.swift +++ b/SousChefAI/ViewModels/CookingModeViewModel.swift @@ -71,7 +71,7 @@ final class CookingModeViewModel: ObservableObject { cameraManager.stopSession() } - func getPreviewLayer() -> AVCaptureVideoPreviewLayer { + func getPreviewLayer() -> AVCaptureVideoPreviewLayer? { cameraManager.previewLayer() } diff --git a/SousChefAI/ViewModels/ScannerViewModel.swift b/SousChefAI/ViewModels/ScannerViewModel.swift index a6a818b..bbe64f1 100644 --- a/SousChefAI/ViewModels/ScannerViewModel.swift +++ b/SousChefAI/ViewModels/ScannerViewModel.swift @@ -25,6 +25,7 @@ final class ScannerViewModel: ObservableObject { nonisolated init(visionService: VisionService = ARVisionService(), cameraManager: CameraManager = CameraManager()) { + print("📱 ScannerViewModel.init() - Creating ViewModel at \(Date())") self.visionService = visionService self.cameraManager = cameraManager } @@ -32,9 +33,12 @@ final class ScannerViewModel: ObservableObject { // MARK: - Camera Management func setupCamera() async { + print("📱 ScannerViewModel.setupCamera() - STARTED at \(Date())") do { try await cameraManager.setupSession() + print("📱 ScannerViewModel.setupCamera() - ✅ SUCCESS at \(Date())") } catch { + print("📱 ScannerViewModel.setupCamera() - ❌ ERROR: \(error)") self.error = error } } @@ -47,8 +51,9 @@ final class ScannerViewModel: ObservableObject { cameraManager.stopSession() } - func getPreviewLayer() -> AVCaptureVideoPreviewLayer { - cameraManager.previewLayer() + func getPreviewLayer() -> AVCaptureVideoPreviewLayer? { + print("📱 ScannerViewModel.getPreviewLayer() - ⚠️ REQUESTING preview layer at \(Date())") + return cameraManager.previewLayer() } // MARK: - Scanning diff --git a/SousChefAI/Views/CookingModeView.swift b/SousChefAI/Views/CookingModeView.swift index bdae471..70c4b47 100644 --- a/SousChefAI/Views/CookingModeView.swift +++ b/SousChefAI/Views/CookingModeView.swift @@ -12,6 +12,7 @@ struct CookingModeView: View { @Environment(\.dismiss) private var dismiss @StateObject private var viewModel: CookingModeViewModel @State private var showingAllSteps = false + @State private var previewLayer: AVCaptureVideoPreviewLayer? init(recipe: Recipe) { _viewModel = StateObject(wrappedValue: CookingModeViewModel(recipe: recipe)) @@ -21,8 +22,8 @@ struct CookingModeView: View { NavigationStack { ZStack { // Camera preview background - if viewModel.isMonitoring { - CameraPreviewView(previewLayer: viewModel.getPreviewLayer()) + if viewModel.isMonitoring, let previewLayer = previewLayer { + CameraPreviewView(previewLayer: previewLayer) .ignoresSafeArea() .opacity(0.3) } @@ -69,6 +70,7 @@ struct CookingModeView: View { } .task { await viewModel.setupCamera() + previewLayer = viewModel.getPreviewLayer() viewModel.startCamera() } .onDisappear { diff --git a/SousChefAI/Views/ScannerView.swift b/SousChefAI/Views/ScannerView.swift index b3f8030..8247e22 100644 --- a/SousChefAI/Views/ScannerView.swift +++ b/SousChefAI/Views/ScannerView.swift @@ -16,9 +16,15 @@ struct ScannerView: View { @State private var detectedPlanes = 0 @State private var lastRaycastResult = "" @State private var showARView = false + @State private var previewLayer: AVCaptureVideoPreviewLayer? + + init() { + print("🔵 ScannerView.init() - View initialized at \(Date())") + } var body: some View { - NavigationStack { + print("🔵 ScannerView.body - Body evaluated at \(Date()), showARView: \(showARView)") + return NavigationStack { ZStack { // AR camera preview or regular camera if showARView { @@ -28,8 +34,17 @@ struct ScannerView: View { ) .ignoresSafeArea() } else { - CameraPreviewView(previewLayer: viewModel.getPreviewLayer()) - .ignoresSafeArea() + if let previewLayer = previewLayer { + CameraPreviewView(previewLayer: previewLayer) + .ignoresSafeArea() + } else { + Color.black + .ignoresSafeArea() + .overlay { + ProgressView("Initializing camera...") + .foregroundStyle(.white) + } + } } // Overlay UI @@ -68,12 +83,19 @@ struct ScannerView: View { } } .task { + print("🔵 ScannerView.task - Task started at \(Date())") if !showARView { + print("🔵 ScannerView.task - Calling setupCamera()") await viewModel.setupCamera() + print("🔵 ScannerView.task - Getting preview layer after setup") + previewLayer = viewModel.getPreviewLayer() + print("🔵 ScannerView.task - Preview layer set: \(previewLayer != nil)") + print("🔵 ScannerView.task - Calling startCamera()") viewModel.startCamera() } } .onDisappear { + print("🔵 ScannerView.onDisappear - Cleaning up at \(Date())") viewModel.cleanup() } .alert("Camera Error", isPresented: .constant(viewModel.error != nil)) { @@ -174,7 +196,11 @@ struct ScannerView: View { withAnimation { showARView.toggle() if !showARView { - viewModel.startCamera() + Task { + await viewModel.setupCamera() + previewLayer = viewModel.getPreviewLayer() + viewModel.startCamera() + } } else { viewModel.stopCamera() } @@ -246,10 +272,12 @@ struct CameraPreviewView: UIViewRepresentable { let previewLayer: AVCaptureVideoPreviewLayer func makeUIView(context: Context) -> UIView { + print("🟢 CameraPreviewView.makeUIView() - Creating preview view at \(Date())") let view = UIView(frame: .zero) view.backgroundColor = .black previewLayer.frame = view.bounds view.layer.addSublayer(previewLayer) + print("🟢 CameraPreviewView.makeUIView() - Preview layer added to view") return view }