Swift
Dimensioning (3D Box Measurement)
VisionSDK ships an optional dimensioning module that measures a real-world box's length, width, height, and volume using the device's LiDAR sensor. Use it to verify package dimensions at a shipping or receiving counter, spot-check cartons before palletization, or capture dimensional data for billing, manifests, or catalog entry without a tape measure.
The module is opt-in: it is not in the default Core install. It lives in the source distribution at packagexlabs/vision-sdk-ios.
First shipped in: VisionSDK iOS v2.2.2 (May 2026).
R&D status: the dimensioning module is in active development. The accuracy envelope, supported shapes, and device requirements may evolve in future SDK releases. Design your integration with that in mind: surface dimensions to your users as approximate, allow re-captures, and don't auto-act on a single capture without confirmation.
5-Minute Quickstart
If you just want to wire it up, the minimum viable integration is four steps:
- Add
pod 'VisionSDK/Dimensioning'(or the matching SPM product) to your project. - Set your Podfile platform to
'17.0'and add the two Info.plist keys below. - Call
await VSDKDimensioning.prefetchModels()once at app launch. - Drop a
VSDKDimensioningViewinto a UIViewController, set yourself as the delegate, callstartRunning(), and read the measurement out of thedidCapturecallback.
The rest of this page is the same flow with much more context: hardware support, configuration, capture conditions and accuracy, error handling, lifecycle, and troubleshooting.
What ships with the SDK
All assets the dimensioning module needs at runtime are bundled with the SDK. You do not need to download anything at first launch and the .offline mode works without a network connection. The .online mode (covered below) is a separate cloud-augmented path that you opt into per session.
Linking the dimensioning module adds a meaningful amount to your app's install size; if size is a concern, gate the install behind a device-capability check at build time, or ship dimensioning in a separate App Clip / extension that only LiDAR-capable users download.
Hardware and OS Requirements
Always check VSDKDimensioning.deviceCapabilities() at runtime before showing any dimensioning entry-point in your UI; do not assume LiDAR from the iOS version or device model.
Installation
The dimensioning module is not included in the binary SPM distribution at packagexlabs/vision-sdk. Install via the source distribution instead.
CocoaPods
In your Podfile:
Then:
The Dimensioning subspec depends on Core, so listing both is redundant but explicit. The subspec declares its own ios.deployment_target = '17.0' and pulls in ARKit and RealityKit as framework dependencies.
Swift Package Manager
Point at vision-sdk-ios directly (not at vision-sdk) and link both products:
If you're using an Xcode .xcodeproj instead of Package.swift, add the package URL via File > Add Package Dependencies, then in your target's Frameworks, Libraries, and Embedded Content section add both VisionSDK and VisionSDKDimensioning.
Verifying the install
A clean build that succeeds plus a successful import VisionSDKDimensioning is enough confirmation.
Info.plist
Add the following keys to the Info.plist of any target that links the dimensioning module:
Replace the strings with copy that's appropriate for your app; users see this string verbatim in the iOS permission prompt. The Core SDK already requires the camera key for scanning, so you only need to add the LiDAR key when you add dimensioning.
The first time your app shows a VSDKDimensioningView, iOS prompts the user for camera permission. If the user denies, all subsequent sessions throw VSDKDimensioningError.arSessionFailed(reason:); there is no second-chance prompt without a Settings round-trip.
App-Launch Setup
Three setup calls at app launch get your app into the right state before any dimensioning UI appears:
Why call prefetchModels()
The first dimensioning session has a cold-start cost the first time it runs on a device. If you let the camera view trigger that work itself, the user may see a slow preview for the first few seconds. prefetchModels() warms that work on a background actor at app launch so the first session opens promptly.
It's safe to fire-and-forget. Re-calls after warm-up are no-ops. Skipping it is non-fatal - the SDK still works, the first session just opens more slowly.
Capability check
VSDKDimensioning.deviceCapabilities() returns a VSDKDimensioningCapabilities value with three booleans:
All three are false on simulators. The most important one to check is lidar - gate the entry-point on it.
UIKit / Objective-C Integration
The UIKit path uses a single UIView subclass (VSDKDimensioningView) and a delegate protocol. It's the simplest integration if your app is UIKit-first or if you want to bridge to Objective-C.
Objective-C
All public types in the dimensioning module are @objc-exposed. The delegate protocol method names are identical; result types (VSDKDimensioningMeasurement, VSDKDimensioningCapabilities) are @objc NSObject subclasses with KVC-accessible properties.
SwiftUI / async-await Integration
Two SwiftUI paths exist: a declarative one-shot view, and a session-backed one for full lifecycle control.
Quick path: VSDKDimensioningSwiftUIView
A view that owns its own session and fires a closure on each captured measurement. Use this when you just want to show a camera and get a result.
Use it from a parent:
Full-control path: VSDKDimensioningSession
The session is a @MainActor ObservableObject exposing @Published lifecycle state. Use it when you want a custom UI on top of the AR view (live track count, manual capture button, custom hints, etc.).
Phase lifecycle
session.phase walks through:
You can bind directly to it in SwiftUI (@Published) for a live status indicator. Use it to drive a "Capture" button that only enables once phase == .detected and disable while .capturing.
session.tracks is also @Published and gives live per-box overlay data (isStable, normalizedScreenRect) for drawing a bounding box on top of the AR view.
Configuration
All knobs live on VSDKDimensioningConfiguration:
Construct it explicitly when you need anything other than centimeters from UIKit:
Offline vs Online mode
.offline runs entirely on-device, no network required, no API key required. .online augments the pipeline with a cloud-side segmentation step that can improve accuracy on harder surfaces; it requires VSDKConstants.apiKey and a network connection at capture time. The measurement.usedCloudSAM flag on the result is true when the cloud path ran.
Default to .offline. Switch to .online only when you specifically need the cloud-augmented accuracy. You can switch modes per-session - they don't share any state.
Capture Conditions and Accuracy
The dimensioning pipeline reaches its typical accuracy only inside an operational envelope. Design your in-app guidance around these limits and re-prompt the user if a capture falls outside them.
Accuracy envelope
- Typical variance: roughly ±3-5 cm per dimension, per capture, on the same box. This comes from LiDAR depth noise, pose drift, and lighting. Treat the result as approximate; surface it to your users as such.
- Minimum box size: ~10 cm on the smallest side. Smaller objects are below the SDK's current tuning floor.
- Shape: cuboids only. The pipeline is designed for rectangular boxes. Tubes, polybags, envelopes, soft bags that sag, and other non-cuboidal shapes are not supported and will fail to capture or return unreliable values.
Geometry and framing
- The whole box must be in view, with the top face visible. Length and width are derived primarily from the top face.
- The box must rest on a flat horizontal surface (table, floor, conveyor) - the SDK uses that surface as the height reference.
- One box at a time. Multiple boxes in frame, or stacked boxes, can confuse the segmentation and may include neighbor edges in the measurement.
Distance and motion
- Optimal capture distance: 40-90 cm from the box. Closer than ~30 cm or farther than ~1.5-2 m degrades accuracy meaningfully.
- Hold steady for 1-2 seconds before and during capture. The pipeline averages multiple frames; motion within the capture window translates directly into error.
- Tilt slightly downward so the camera sees the top face plus at least one side face. Pure top-down or pure side-on shots give the pipeline less geometric information.
Surface and lighting
- Box surfaces: prefer flat, opaque, matte cardboard. Highly reflective wrap, transparent film, and very dark matte surfaces reduce LiDAR signal quality.
- Lighting: ambient indoor light or diffuse warehouse lighting is ideal. Very dim environments hurt detection. Direct sunlight on glossy boxes can introduce false depth readings.
- Background: cluttered backgrounds (boxes stacked behind the target, shelving directly behind) can cause the SDK to include background edges. A contrasting empty surface behind the box helps.
Recommended UX guidance
If you build a customer-facing dimensioning flow, the following patterns hold up well:
- Show a live framing hint that asks the user to move closer if the box occupies less than ~30% of the screen, or farther if it occupies more than ~80%.
- Require a stable detection before enabling the capture button (use
phase == .detectedandtrack.isStablefrom the session path). - Offer a one-tap "Re-capture" - because of the ±3-5 cm variance, averaging two or three captures noticeably tightens the result.
- Confirm with the user before persisting a measurement to a downstream system (billing, manifest, catalog).
Result Type
VSDKDimensioningMeasurement (also @objc NSObject):
Reading values
Confidence
measurement.confidence is a Float in [0, 1]. Use it to gate auto-save: a typical threshold is 0.85 for unattended capture. Below that, prompt the user to retry from a better angle.
Live tracks (session path only)
session.tracks is an array of VSDKDimensioningTrack:
Use normalizedScreenRect to draw an overlay (multiply by your view's bounds). isStable flips to true once the box has been tracked long enough for the SDK to attempt a capture.
Errors
All cases use domain = "io.packagex.visionsdk.dimensioning". From UIKit, the optional didFailWithError: delegate receives NSError. From the async path, errors throw as VSDKDimensioningError; catch them with catch let err as VSDKDimensioningError.
Example: handling the full error set
Important: Capture Session Conflict
VSDKDimensioningView and VSDKDimensioningSession own an ARSession internally. ARKit and AVCaptureSession cannot share the camera, so:
- Before showing the dimensioning view, stop any existing
CodeScannerView(call its scanning teardown), then present the dimensioning UI. - Before returning to the barcode/OCR scanner, call
deConfigure()(UIKit) or let the SwiftUI session deinit (it stops the AR session indeinit).
If both are alive simultaneously, one of them will fail to start with arSessionFailed. There is no automatic handoff - your navigation code must serialize them.
A common pattern in a single-screen flow with both:
Troubleshooting
Build succeeds but the first session fails on launch
Confirm pod install or SPM resolution completed cleanly and that VisionSDKDimensioning is linked in your target's Frameworks, Libraries, and Embedded Content. If you still see issues, do a clean build (Cmd-Shift-K) and reset the Xcode package cache.
Camera opens, AR session starts, but no boxes are detected
This is most often a cold-start race: the SDK has not finished warming up. Either:
- Add
await VSDKDimensioning.prefetchModels()at app launch (recommended), or - Wait a few seconds in the view before expecting detections, or
- Filter Xcode console logs for
[VSDK-DIM]- the SDK prints diagnostic progress messages.
If you've warmed up and still see no detection, check the capture conditions: framing, distance, surface, and lighting. Highly reflective wrap, transparent film, very dark matte surfaces, or boxes smaller than ~10 cm are below the pipeline's tuning floor.
"Scanning Environment..." or initialization phase doesn't progress
ARKit world-tracking is still establishing. Pan the device slowly side to side and downward to give it more visual texture to anchor on. Ensure the scene has enough light. If phase stays at .initializing for more than a few seconds, the user is likely pointing the camera at a textureless surface (a blank wall) - re-prompt them to point at the floor / their workspace.
Dimensions look wildly wrong (e.g. 1 cm height)
The SDK locked onto the box's own top face as the floor plane. This usually happens when the user starts the session already aiming at the box without giving ARKit a chance to see the floor first. Workaround: re-capture after panning the camera over the supporting surface (floor / table) for a second, then back to the box.
Capture / measure path stays disabled
The pipeline has not reached a stable detection. Wait 1-2 seconds with the box centered, the whole box in frame, and the device steady. If this persists, the capture conditions are out of envelope - see "Capture Conditions and Accuracy" above.
lidarUnavailable on what should be a LiDAR device
Devices in some lighting conditions return spurious false from ARKit's capability probe. Confirm by running Apple's "Object Capture" or "Measure" apps. If those work but the SDK says no LiDAR, file an issue with the device model, iOS version, and the SDK version.
noGroundPlane
The dimensioning pipeline expects a flat floor plane before locking onto a box. Pan the camera at the floor for ~1 second before pointing at the box. This is also a hint to surface to your user via UI ("Point at the floor...").
Measurements jitter / change a lot
Typical capture variance is roughly ±3-5 cm per dimension; if you're seeing more, three things help:
- Keep the device steady for a full 1-2 seconds before and during capture. The pipeline averages frames - any motion in the capture window translates directly into error.
- Ensure all four top corners of the box are in frame.
- Avoid extreme lighting (direct sunlight on glossy tape; near-dark warehouses).
- Move the device into the 40-90 cm sweet spot. Very close (<30 cm) or very far (>1.5 m) captures degrade meaningfully.
- Confirm the box is on a flat horizontal surface and there are no neighboring boxes within ~10 cm of its edges.
Online mode is slow or unreliable
.online makes a per-capture HTTP request to PackageX cloud. On poor connections, prefer .offline and pick .online only for a manual "high-accuracy retry" button.
Both barcode scanning and dimensioning fail to start
Almost always the capture-session conflict (see "Capture Session Conflict" above). Tear down one before starting the other.
FAQ
Do I need a separate API key for dimensioning?
No. VSDKConstants.apiKey is the same key used by the Core SDK's OCR features. You only need to set it for .online mode.
Will the dimensioning feature work on iPad?
Yes, on LiDAR-equipped iPads (iPad Pro 2020 / 4th gen and newer). The same deviceCapabilities().lidar check applies.
Can I use ARKit elsewhere in my app while dimensioning is open?
Not for the same camera. Two ARSessions cannot share the rear camera. Tear down one before starting the other.
Can the SDK measure cylindrical or irregular shapes? The dimensioning module is designed for rectangular boxes (cuboids). Tubes, polybags, envelopes, and soft bags that sag are not supported and will either fail to capture or return unreliable values.
What's the smallest box the SDK can measure? The pipeline is currently tuned for parcels larger than about 10 cm on the smallest side. Smaller items are below the tuning floor and produce unreliable measurements.
How accurate is a single measurement? Plan around roughly ±3-5 cm of variance per dimension. The variance is centered around the true value, so averaging two or three captures of the same box tightens the result. See "Capture Conditions and Accuracy" above for the full operational envelope.
What units does the volume property return? volume is always an NSMeasurement with unit UnitVolume.cubicMeters, derived from length * width * height. Convert as needed (measurement.volume.converting(to: .cubicCentimeters)).