Lab 051 – Issues with World Tracking

Documenting an issue with world tracking.

I’ve been getting ready to work on a series of Example Code posts about ARKit WorldTracking. I’ve been having some issues removing anchors .

I can add anchors with no issue. They will be there the next time I run the scene. I can also get updates when ARKit sends them. I can remove anchors, but not all the time. The method I’m using is to call removeAnchor() on the data provider.

worldTracking.removeAnchor(forID: uuid)
// Yes, I have also tried `removeAnchor(_ worldAnchor: WorldAnchor)`

This works if there are more than one anchor in a scene. When I’m down to one remaining anchor, I can remove it. It seems to succeed (does not raise an error) but the next time I run the scene the removed anchor is back. This only happens when there is only one remaining anchor.

do {
  // This always run, but it doesn't seem to "save" the removal when there is only one anchor left.
  try await worldTracking.removeAnchor(forID: uuid)
} catch {
  // I have never seen this block fire!
  print("Failed to remove world anchor \(uuid) with error: \(error).")
}

Please watch this video for a detailed example.

Here is the full code. Can you see if I’m doing something wrong? Is this a bug?

struct Lab051: View {
    @State var session = ARKitSession()
    @State var worldTracking = WorldTrackingProvider()

    @State var worldAnchorEntities: [UUID: Entity] = [:]

    @State var placement = Entity()

    @State var subject : ModelEntity = {
        let subject = ModelEntity(
            mesh: .generateSphere(radius: 0.06),
            materials: [SimpleMaterial(color: .stepRed, isMetallic: false)])
        subject.setPosition([0, 0, 0], relativeTo: nil)

        let collision = CollisionComponent(shapes: [.generateSphere(radius: 0.06)])
        let input = InputTargetComponent()
        subject.components.set([collision, input])

        return subject
    }()

    var body: some View {
        RealityView { content in

            guard let scene = try? await Entity(named: "WorldTracking", in: realityKitContentBundle) else { return }
            content.add(scene)

            if let placementEntity = scene.findEntity(named: "PlacementPreview") {
                placement = placementEntity
            }

        } update: { content in
            for (_, entity) in worldAnchorEntities {
                if !content.entities.contains(entity) {
                    content.add(entity)
                }
            }
        }
        .modifier(DragGestureImproved())
        .gesture(tapGesture)
        .task {
            try! await setupAndRunWorldTracking()
        }
    }

    var tapGesture: some Gesture {
        TapGesture()
            .targetedToAnyEntity()
            .onEnded { value in

                if value.entity.name == "PlacementPreview" {
                    // If we tapped the placement preview cube, create an anchor
                    Task {
                        let anchor = WorldAnchor(originFromAnchorTransform: value.entity.transformMatrix(relativeTo: nil))
                        try await worldTracking.addAnchor(anchor)
                    }
                } else {
                    Task {
                        // Get the UUID we stored on the entity
                        let uuid = UUID(uuidString: value.entity.name) ?? UUID()

                        do {
                            try await worldTracking.removeAnchor(forID: uuid)
                        } catch {
                            print("Failed to remove world anchor \(uuid) with error: \(error).")
                        }

                    }
                }
            }
    }

    func setupAndRunWorldTracking() async throws {

        if WorldTrackingProvider.isSupported {
            do {
                try await session.run([worldTracking])

                for await update in worldTracking.anchorUpdates {
                    switch update.event {
                    case .added:

                        let subjectClone = subject.clone(recursive: true)
                        subjectClone.isEnabled = true
                        subjectClone.name = update.anchor.id.uuidString
                        subjectClone.transform = Transform(matrix: update.anchor.originFromAnchorTransform)

                        worldAnchorEntities[update.anchor.id] = subjectClone
                        print("🟢 Anchor added \(update.anchor.id)")

                    case .updated:

                        guard let entity = worldAnchorEntities[update.anchor.id] else {
                            print("No entity found to update for anchor \(update.anchor.id)")
                            return
                        }

                        entity.transform = Transform(matrix: update.anchor.originFromAnchorTransform)

                        print("🔵 Anchor updated \(update.anchor.id)")

                    case .removed:

                        worldAnchorEntities[update.anchor.id]?.removeFromParent()
                        worldAnchorEntities.removeValue(forKey: update.anchor.id)
                        print("🔴 Anchor removed \(update.anchor.id)")

                        if let remainingAnchors = await worldTracking.allAnchors {
                            print("Remaining Anchors: \(remainingAnchors.count)")
                        }
                    }
                }
            } catch {
                print("ARKit session error \(error)")
            }
        }
    }
}

Support our work so we can continue to bring you new examples and articles.

Download the Xcode project with this and many more labs from Step Into Vision.

Questions or feedback?

3 Comments

  1. I am having problem in testing out this lab. Sometimes, when I tap on cube, the red sphere not appears. Similarly, when it appears and I add two or more spheres. Then when I close the app and relaunch, sometimes I cant see any of red spheres (which was previously generated and saved) and sometimes I can see a lot of red spheres which I think generated when I tapped on cube (but at that time, I cant see the red sphere that appeared).

    its like this:
    case no. 1: When anchors are saved, I relaunch the app, sometimes I cant see those saved anchors. While in this state, if I tap a cube, the red sphere not be generated or displayed.

    case no. 2 However, when I opened the app again, I can see all the previous spheres along with the ones that are generated but not visible at the time of case no.1.

    Can you please guide? I really appreciate that.

    1. Hi, we shared this lab to report issues like this to Apple and document them. This code is just for reference for that report. Put another way, the code in this lab is broken and was published in the context of looking for those answers.