fixed race condition
This commit is contained in:
@@ -18,6 +18,16 @@ final class CameraManager: NSObject, ObservableObject {
|
|||||||
@Published var error: CameraError?
|
@Published var error: CameraError?
|
||||||
@Published var isRunning = false
|
@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 let captureSession = AVCaptureSession()
|
||||||
nonisolated(unsafe) private var videoOutput: AVCaptureVideoDataOutput?
|
nonisolated(unsafe) private var videoOutput: AVCaptureVideoDataOutput?
|
||||||
private let videoQueue = DispatchQueue(label: "com.souschef.video", qos: .userInitiated)
|
private let videoQueue = DispatchQueue(label: "com.souschef.video", qos: .userInitiated)
|
||||||
@@ -55,13 +65,22 @@ final class CameraManager: NSObject, ObservableObject {
|
|||||||
// MARK: - Session Setup
|
// MARK: - Session Setup
|
||||||
|
|
||||||
func setupSession() async throws {
|
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
|
// Only configure once
|
||||||
guard !isConfigured else {
|
guard !isConfigured else {
|
||||||
print("🎥 CameraManager.setupSession() - Already configured, returning")
|
print("🎥 CameraManager.setupSession() - Already configured, returning")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
sessionState = .configuring
|
||||||
|
|
||||||
// Ensure authorization is checked first
|
// Ensure authorization is checked first
|
||||||
await checkAuthorization()
|
await checkAuthorization()
|
||||||
|
|
||||||
@@ -112,13 +131,20 @@ final class CameraManager: NSObject, ObservableObject {
|
|||||||
|
|
||||||
captureSession.commitConfiguration()
|
captureSession.commitConfiguration()
|
||||||
isConfigured = true
|
isConfigured = true
|
||||||
|
sessionState = .configured
|
||||||
print("🎥 CameraManager.setupSession() - ✅ COMPLETED successfully at \(Date())")
|
print("🎥 CameraManager.setupSession() - ✅ COMPLETED successfully at \(Date())")
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: - Session Control
|
// MARK: - Session Control
|
||||||
|
|
||||||
func startSession() {
|
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
|
let session = captureSession
|
||||||
Task.detached { [weak self] in
|
Task.detached { [weak self] in
|
||||||
@@ -126,12 +152,20 @@ final class CameraManager: NSObject, ObservableObject {
|
|||||||
|
|
||||||
await MainActor.run { [weak self] in
|
await MainActor.run { [weak self] in
|
||||||
self?.isRunning = true
|
self?.isRunning = true
|
||||||
|
self?.sessionState = .running
|
||||||
|
print("🎥 CameraManager.startSession() - ✅ Session running")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func stopSession() {
|
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
|
let session = captureSession
|
||||||
Task.detached { [weak self] in
|
Task.detached { [weak self] in
|
||||||
@@ -139,10 +173,36 @@ final class CameraManager: NSObject, ObservableObject {
|
|||||||
|
|
||||||
await MainActor.run { [weak self] in
|
await MainActor.run { [weak self] in
|
||||||
self?.isRunning = false
|
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
|
// MARK: - Frame Stream
|
||||||
|
|
||||||
func frameStream() -> AsyncStream<CVPixelBuffer> {
|
func frameStream() -> AsyncStream<CVPixelBuffer> {
|
||||||
|
|||||||
@@ -175,8 +175,10 @@ final class ScannerViewModel: ObservableObject {
|
|||||||
|
|
||||||
// MARK: - Cleanup
|
// MARK: - Cleanup
|
||||||
|
|
||||||
func cleanup() {
|
func cleanup() async {
|
||||||
|
print("📱 ScannerViewModel.cleanup() - Starting cleanup")
|
||||||
stopScanning()
|
stopScanning()
|
||||||
stopCamera()
|
await cameraManager.cleanup()
|
||||||
|
print("📱 ScannerViewModel.cleanup() - ✅ Cleanup complete")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -96,7 +96,9 @@ struct ScannerView: View {
|
|||||||
}
|
}
|
||||||
.onDisappear {
|
.onDisappear {
|
||||||
print("🔵 ScannerView.onDisappear - Cleaning up at \(Date())")
|
print("🔵 ScannerView.onDisappear - Cleaning up at \(Date())")
|
||||||
viewModel.cleanup()
|
Task {
|
||||||
|
await viewModel.cleanup()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
.alert("Camera Error", isPresented: .constant(viewModel.error != nil)) {
|
.alert("Camera Error", isPresented: .constant(viewModel.error != nil)) {
|
||||||
Button("OK") {
|
Button("OK") {
|
||||||
|
|||||||
Reference in New Issue
Block a user