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
}
}
}