fixed race condition
This commit is contained in:
@@ -18,6 +18,16 @@ final class CameraManager: NSObject, ObservableObject {
|
||||
@Published var error: CameraError?
|
||||
@Published var isRunning = false
|
||||
|
||||
enum SessionState {
|
||||
case idle
|
||||
case configuring
|
||||
case configured
|
||||
case starting
|
||||
case running
|
||||
case stopping
|
||||
}
|
||||
@Published private(set) var sessionState: SessionState = .idle
|
||||
|
||||
nonisolated(unsafe) private let captureSession = AVCaptureSession()
|
||||
nonisolated(unsafe) private var videoOutput: AVCaptureVideoDataOutput?
|
||||
private let videoQueue = DispatchQueue(label: "com.souschef.video", qos: .userInitiated)
|
||||
@@ -55,13 +65,22 @@ final class CameraManager: NSObject, ObservableObject {
|
||||
// MARK: - Session Setup
|
||||
|
||||
func setupSession() async throws {
|
||||
print("🎥 CameraManager.setupSession() - STARTED at \(Date())")
|
||||
print("🎥 CameraManager.setupSession() - STARTED at \(Date()), current state: \(sessionState)")
|
||||
|
||||
// Wait if session is stopping
|
||||
if sessionState == .stopping {
|
||||
print("🎥 CameraManager.setupSession() - ⏳ Waiting for session to finish stopping...")
|
||||
await waitForSessionToStop()
|
||||
}
|
||||
|
||||
// Only configure once
|
||||
guard !isConfigured else {
|
||||
print("🎥 CameraManager.setupSession() - Already configured, returning")
|
||||
return
|
||||
}
|
||||
|
||||
sessionState = .configuring
|
||||
|
||||
// Ensure authorization is checked first
|
||||
await checkAuthorization()
|
||||
|
||||
@@ -112,13 +131,20 @@ final class CameraManager: NSObject, ObservableObject {
|
||||
|
||||
captureSession.commitConfiguration()
|
||||
isConfigured = true
|
||||
sessionState = .configured
|
||||
print("🎥 CameraManager.setupSession() - ✅ COMPLETED successfully at \(Date())")
|
||||
}
|
||||
|
||||
// MARK: - Session Control
|
||||
|
||||
func startSession() {
|
||||
guard !captureSession.isRunning else { return }
|
||||
guard !captureSession.isRunning else {
|
||||
print("🎥 CameraManager.startSession() - Session already running")
|
||||
return
|
||||
}
|
||||
|
||||
print("🎥 CameraManager.startSession() - Starting session")
|
||||
sessionState = .starting
|
||||
|
||||
let session = captureSession
|
||||
Task.detached { [weak self] in
|
||||
@@ -126,12 +152,20 @@ final class CameraManager: NSObject, ObservableObject {
|
||||
|
||||
await MainActor.run { [weak self] in
|
||||
self?.isRunning = true
|
||||
self?.sessionState = .running
|
||||
print("🎥 CameraManager.startSession() - ✅ Session running")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func stopSession() {
|
||||
guard captureSession.isRunning else { return }
|
||||
guard captureSession.isRunning else {
|
||||
print("🎥 CameraManager.stopSession() - Session not running")
|
||||
return
|
||||
}
|
||||
|
||||
print("🎥 CameraManager.stopSession() - Stopping session")
|
||||
sessionState = .stopping
|
||||
|
||||
let session = captureSession
|
||||
Task.detached { [weak self] in
|
||||
@@ -139,10 +173,36 @@ final class CameraManager: NSObject, ObservableObject {
|
||||
|
||||
await MainActor.run { [weak self] in
|
||||
self?.isRunning = false
|
||||
self?.sessionState = .idle
|
||||
print("🎥 CameraManager.stopSession() - ✅ Session stopped")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Helper Methods
|
||||
|
||||
private func waitForSessionToStop() async {
|
||||
// Wait for session to reach idle state
|
||||
while sessionState == .stopping {
|
||||
try? await Task.sleep(for: .milliseconds(100))
|
||||
}
|
||||
print("🎥 CameraManager.waitForSessionToStop() - ✅ Session is now idle")
|
||||
}
|
||||
|
||||
/// Cleanup method that ensures session is fully stopped before returning
|
||||
func cleanup() async {
|
||||
print("🎥 CameraManager.cleanup() - Starting cleanup")
|
||||
|
||||
if captureSession.isRunning {
|
||||
stopSession()
|
||||
}
|
||||
|
||||
// Wait for session to fully stop
|
||||
await waitForSessionToStop()
|
||||
|
||||
print("🎥 CameraManager.cleanup() - ✅ Cleanup complete")
|
||||
}
|
||||
|
||||
// MARK: - Frame Stream
|
||||
|
||||
func frameStream() -> AsyncStream<CVPixelBuffer> {
|
||||
|
||||
@@ -175,8 +175,10 @@ final class ScannerViewModel: ObservableObject {
|
||||
|
||||
// MARK: - Cleanup
|
||||
|
||||
func cleanup() {
|
||||
func cleanup() async {
|
||||
print("📱 ScannerViewModel.cleanup() - Starting cleanup")
|
||||
stopScanning()
|
||||
stopCamera()
|
||||
await cameraManager.cleanup()
|
||||
print("📱 ScannerViewModel.cleanup() - ✅ Cleanup complete")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -96,7 +96,9 @@ struct ScannerView: View {
|
||||
}
|
||||
.onDisappear {
|
||||
print("🔵 ScannerView.onDisappear - Cleaning up at \(Date())")
|
||||
viewModel.cleanup()
|
||||
Task {
|
||||
await viewModel.cleanup()
|
||||
}
|
||||
}
|
||||
.alert("Camera Error", isPresented: .constant(viewModel.error != nil)) {
|
||||
Button("OK") {
|
||||
|
||||
Reference in New Issue
Block a user