ios – Why is my ObjectCaptureSession crashing once I name proceedToNextScanPass()?

ios – Why is my ObjectCaptureSession crashing once I name proceedToNextScanPass()?


I’m trying to construct an app that makes use of the ObjectCapture API, however I am unable to get previous this bug. The app crashes 9/10 occasions once I name proceedToNextScanPass(), when you might please counsel some attainable options it will be appreciated.

I am following the Apple Developer Pattern Challenge just about all the way down to a tee so I am unsure what is going on improper. There’s additionally no strategy to throw an error when utilizing proceedToNextScanPass() so it is tough to debug.

Right here is my AppDataModel, like within the pattern venture.

@MainActor
@Observable
class AppDataModel: ObservableObject {
    static let occasion = AppDataModel()

    enum ModelState {
        case notSet
        case prepared
        case capturing
        case prepareToReconstruct
        case reconstructing
        case viewing
        case accomplished
        case failed
    }

    var state: ModelState = .notSet {
        didSet {
            performStateTransition(from: oldValue, to: state)
        }
    }
    var captureFolderManager: CaptureFolderManager?
    var objectCaptureSession: ObjectCaptureSession?
    var photogrammetrySession: PhotogrammetrySession?

    var orbit: Int = 0

    personal init() {
        state = .prepared
    }

    func endCapture() {
        state = .accomplished
    }

    func removeCaptureFolder() {
        guard let url = captureFolderManager?.captureFolder else { return }
        attempt? FileManager.default.removeItem(at: url)
    }

    personal func performStateTransition(from fromState: ModelState, to toState: ModelState) {
        if fromState == toState { return }

        swap toState {
        case .prepared:
            do {
                attempt startNewCapture()
            } catch {
                print("Beginning new seize failed!")
            }
        case .prepareToReconstruct:
            objectCaptureSession = nil
            do {
                attempt startReconstruction()
            } catch {
                print("Reconstruction failed!")
            }
        default:
            break
        }
    }

    personal func startReconstruction() throws {
        var configuration = PhotogrammetrySession.Configuration()

        guard let captureFolderManager else {
            preconditionFailure("CaptureFolderManager unexpectedly nil!")
        }

        configuration.checkpointDirectory = captureFolderManager.checkpointFolder
        photogrammetrySession = attempt PhotogrammetrySession(
            enter: captureFolderManager.imagesFolder,
            configuration: configuration
        )

        state = .reconstructing
    }
}

extension AppDataModel {
    personal func startNewCapture() throws {
        captureFolderManager = attempt CaptureFolderManager()
        objectCaptureSession = ObjectCaptureSession()

        guard let captureFolderManager, let session = objectCaptureSession else {
            throw NSError(area: "AppDataModel", code: 1, userInfo: [NSLocalizedDescriptionKey: "Failed to initialize session or folder manager"])
        }

        var configuration = ObjectCaptureSession.Configuration()
        configuration.isOverCaptureEnabled = true
        configuration.checkpointDirectory = captureFolderManager.checkpointFolder
        session.begin(imagesDirectory: captureFolderManager.imagesFolder, configuration: configuration)

        state = .capturing
    }

    personal func reset() {
        objectCaptureSession = nil
        captureFolderManager = nil
        state = .prepared
    }
}

In addition to the primary view the place I am making the decision to subsequent scan move

struct CapturePrimaryView: View {
    @Surroundings(AppDataModel.self) var appModel
    var session: ObjectCaptureSession
    @State personal var currentOrbit = 1

    var physique: some View {
        ZStack {
            if session.userCompletedScanPass {
                if currentOrbit <= 2 {
                    Button("Proceed") {
                        proceedToNextScanPass()
                    }
                    .buttonStyle(.borderedProminent)
                    .tint(.yellow)
                } else if currentOrbit == 3 {
                    Button("End") {
                        completeCapture()
                    }
                    .buttonStyle(.borderedProminent)
                }
            } else {
                ObjectCaptureView(session: session, cameraFeedOverlay: { GradientBackground() })
                    .hideObjectReticle(false)
                    .transition(.opacity)
            }
        }
        .onAppear {
            UIApplication.shared.isIdleTimerDisabled = true
        }
        .onChange(of: session.state) { _, newValue in
            print("Session State Modified: (newValue)")
        }
        .id(session.id)
    }

    personal func proceedToNextScanPass() {
        guard session.state == .capturing else {
            print("Error: Can't start a brand new scan move as a result of the session will not be in capturing state. Present state: (session.state)")
            return
        }

        session.beginNewScanPass()
        currentOrbit += 1
        print("New scan move began efficiently. Present orbit: (currentOrbit)")
    }

    personal func completeCapture() {
        appModel.state = .accomplished
    }
}

And my primary ContentView

struct ContentView: View {
    @Surroundings(AppDataModel.self) var appModel
    @State personal var hasDetectionFailed = false
    @State personal var showReconstructionView = false

    var physique: some View {
        VStack {
            if appModel.state == .capturing {
                if let session = appModel.objectCaptureSession {
                    CapturePrimaryView(session: session)
                        .overlay {
                            Spacer()
                            
                            CaptureButton(session: session, hasDetectionFailed: $hasDetectionFailed)
                        }
                }
            } else {
                Textual content("Clean")
            }
        }
        .onAppear(carry out: {
            UIApplication.shared.isIdleTimerDisabled = true
        })
        .onDisappear(carry out: {
            UIApplication.shared.isIdleTimerDisabled = false
        })
        .sheet(isPresented: $showReconstructionView) {
            if let _ = appModel.captureFolderManager {
                Textual content("Reconstruct")
            }
        }
    }
}

@MainActor
personal struct CaptureButton: View {
    @Surroundings(AppDataModel.self) var appModel
    var session: ObjectCaptureSession
    @Binding var hasDetectionFailed: Bool
    @State personal var present = true
    
    var physique: some View {
        ZStack {
            if present {
                Button("Proceed") {
                    performAction()
                }
                .daring()
                .buttonStyle(.borderedProminent)
            }
        }
    }
    
    personal func performAction() {
        if session.state == .prepared {
            hasDetectionFailed = !(session.startDetecting())
        } else if case .detecting = session.state {
            session.startCapturing()
            present = false
        }
    }
}

Leave a Reply

Your email address will not be published. Required fields are marked *