I’m making an attempt to construct an app that makes use of the ObjectCapture API, however I can not get previous this bug. The app crashes 9/10 instances once I name proceedToNextScanPass()
, if you happen to might please recommend some attainable options it could be appreciated.
I am following the Apple Developer Pattern Venture just about right down to a tee so I am unsure what is going on mistaken. There’s additionally no solution to throw an error when utilizing proceedToNextScanPass()
so it is troublesome to debug.
Right here is my AppDataModel, like within the pattern mission.
@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 principle view the place I am making the decision to subsequent scan cross
struct CapturePrimaryView: View {
@Atmosphere(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 cross as a result of the session shouldn't be in capturing state. Present state: (session.state)")
return
}
session.beginNewScanPass()
currentOrbit += 1
print("New scan cross began efficiently. Present orbit: (currentOrbit)")
}
personal func completeCapture() {
appModel.state = .accomplished
}
}
And my foremost ContentView
struct ContentView: View {
@Atmosphere(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 {
@Atmosphere(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
}
}
}