1. Swift
  2. Dimensioning (3D Box Measurement)

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:

  1. Add pod 'VisionSDK/Dimensioning' (or the matching SPM product) to your project.
  2. Set your Podfile platform to '17.0' and add the two Info.plist keys below.
  3. Call await VSDKDimensioning.prefetchModels() once at app launch.
  4. Drop a VSDKDimensioningView into a UIViewController, set yourself as the delegate, call startRunning(), and read the measurement out of the didCapture callback.

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

Requirement Value Notes
iOS deployment target 17.0+ The dimensioning module raises the Core SDK's iOS 13 minimum.
Device class LiDAR-equipped iPhone 12 Pro / 13 Pro / 14 Pro / 15 Pro / 16 Pro (and successors). iPad Pro 2020 (4th gen) and later.
Simulator Not supported VSDKDimensioning.deviceCapabilities().lidar returns false; any attempt to start a session throws VSDKDimensioningError.lidarUnavailable.
Xcode 15.0+ Required for iOS 17 SDK.
Swift 5.9+

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:

        platform :ios, '17.0'

target 'MyApp' do
  use_frameworks!
  pod 'VisionSDK/Core'
  pod 'VisionSDK/Dimensioning'
end

      

Then:

        pod install

      

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:

        // Package.swift
let package = Package(
    name: "MyApp",
    platforms: [.iOS(.v17)],
    dependencies: [
        .package(
            url: "https://github.com/packagexlabs/vision-sdk-ios.git",
            from: "2.2.2"
        )
    ],
    targets: [
        .target(
            name: "MyApp",
            dependencies: [
                .product(name: "VisionSDK",             package: "vision-sdk-ios"),
                .product(name: "VisionSDKDimensioning", package: "vision-sdk-ios"),
            ]
        )
    ]
)

      

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:

        <key>NSCameraUsageDescription</key>
<string>Required for box dimensioning</string>
<key>Privacy - LiDAR Usage Description</key>
<string>Required for accurate 3D measurement</string>

      

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:

        import VisionSDK
import VisionSDKDimensioning

@main
struct MyApp: App {
    init() {
        // 1. Set your API key (only required for .online mode)
        VSDKConstants.apiKey = "YOUR_API_KEY"

        // 2. Check capability before showing any dimensioning entry-point
        let caps = VSDKDimensioning.deviceCapabilities()
        if !caps.lidar {
            // On non-LiDAR devices, hide the dimensioning button/screen entirely.
            // Do not let the user navigate to it.
        }

        // 3. Pre-warm CoreML so the first session opens instantly
        Task { await VSDKDimensioning.prefetchModels() }
    }

    var body: some Scene {
        WindowGroup { RootView() }
    }
}

      

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.

        Task { await VSDKDimensioning.prefetchModels() }

      

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:

Field What true means
lidar Device has a usable LiDAR sensor
arWorldTracking ARKit world-tracking is available (always true on supported devices)
sceneReconstruction Scene reconstruction is supported (used internally)

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.

        import UIKit
import VisionSDKDimensioning

final class DimensioningViewController: UIViewController, VSDKDimensioningViewDelegate {

    private let dimView = VSDKDimensioningView()

    override func viewDidLoad() {
        super.viewDidLoad()

        // 1. Add as a full-bleed subview
        dimView.translatesAutoresizingMaskIntoConstraints = false
        view.addSubview(dimView)
        NSLayoutConstraint.activate([
            dimView.topAnchor.constraint(equalTo: view.topAnchor),
            dimView.bottomAnchor.constraint(equalTo: view.bottomAnchor),
            dimView.leadingAnchor.constraint(equalTo: view.leadingAnchor),
            dimView.trailingAnchor.constraint(equalTo: view.trailingAnchor),
        ])

        // 2. Configure with mode and tracking cap. Measurement unit is hardcoded to
        //    centimeters in this convenience initializer; use VSDKDimensioningSession
        //    for other units.
        dimView.configure(
            delegate: self,
            mode: .offline,
            maximumTrackCount: 3
        )

        // 3. Start the AR session and camera
        dimView.startRunning()
    }

    // MARK: VSDKDimensioningViewDelegate (required)

    func dimensioningView(
        _ view: VSDKDimensioningView,
        didCapture measurement: VSDKDimensioningMeasurement
    ) {
        let l = measurement.length.doubleValue   // cm
        let w = measurement.width.doubleValue
        let h = measurement.height.doubleValue
        print("Captured \(l) x \(w) x \(h) cm, confidence \(measurement.confidence)")
        // Pop this VC, save the measurement, etc.
    }

    // MARK: VSDKDimensioningViewDelegate (optional)

    func dimensioningView(
        _ view: VSDKDimensioningView,
        didFailWithError error: NSError
    ) {
        // error.domain == "io.packagex.visionsdk.dimensioning"
        // See the Errors table below for codes.
        print("Dimensioning error: \(error.code) - \(error.localizedDescription)")
    }

    // MARK: Teardown

    override func viewWillDisappear(_ animated: Bool) {
        super.viewWillDisappear(animated)
        // Releases the ARSession and stops the camera. Always call this when
        // leaving the screen; AVCaptureSession will fight ARSession otherwise.
        dimView.deConfigure()
    }
}

      

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.

        #import <VisionSDKDimensioning/VisionSDKDimensioning-Swift.h>

VSDKDimensioningView *dimView = [[VSDKDimensioningView alloc] init];
[dimView configureWithDelegate:self
                          mode:VSDKDimensioningModeOffline
              maximumTrackCount:3];
[dimView startRunning];

      

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.

        import SwiftUI
import VisionSDKDimensioning

struct DimensioningCameraScreen: View {
    var onMeasured: (VSDKDimensioningMeasurement) -> Void

    var body: some View {
        VSDKDimensioningSwiftUIView(
            configuration: .init(
                mode: .offline,
                measurementUnit: .centimeters,
                maximumTrackCount: 3
            ),
            onCapture: onMeasured
        )
        .ignoresSafeArea()
    }
}

      

Use it from a parent:

        struct ContentView: View {
    @State private var showCamera = false
    @State private var lastMeasurement: VSDKDimensioningMeasurement?

    var body: some View {
        VStack {
            if let m = lastMeasurement {
                Text("\(m.length.doubleValue) x \(m.width.doubleValue) x \(m.height.doubleValue) cm")
            }
            Button("Measure box") { showCamera = true }
        }
        .fullScreenCover(isPresented: $showCamera) {
            DimensioningCameraScreen { measurement in
                lastMeasurement = measurement
                showCamera = false
            }
        }
    }
}

      

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.).

        import SwiftUI
import VisionSDKDimensioning

@MainActor
final class DimensioningSessionModel: ObservableObject {

    let session = VSDKDimensioningSession(
        configuration: VSDKDimensioningConfiguration(
            mode: .offline,
            measurementUnit: .centimeters,
            maximumTrackCount: 5
        )
    )

    @Published var lastMeasurement: VSDKDimensioningMeasurement?
    @Published var lastError: VSDKDimensioningError?

    func start() async {
        do {
            try await session.start()
        } catch let error as VSDKDimensioningError {
            lastError = error
        } catch {
            print("unexpected", error)
        }
    }

    func capture() async {
        do {
            let m = try await session.capture()
            lastMeasurement = m
        } catch let error as VSDKDimensioningError {
            lastError = error
        } catch {
            print("unexpected", error)
        }
    }
}

struct DimensioningSessionView: View {
    @StateObject private var model = DimensioningSessionModel()

    var body: some View {
        ZStack {
            // The AR camera view, bound to this session's configuration
            VSDKDimensioningSwiftUIView(configuration: model.session.configuration)
                .ignoresSafeArea()

            VStack {
                Spacer()
                // Bind to the @Published phase for live UI feedback
                Text(phaseLabel(model.session.phase))
                    .padding()
                    .background(.thinMaterial)
                    .clipShape(Capsule())

                Button {
                    Task { await model.capture() }
                } label: {
                    Image(systemName: "camera.aperture")
                        .resizable()
                        .frame(width: 64, height: 64)
                }
                .padding(.bottom, 40)
            }
        }
        .task { await model.start() }
    }

    private func phaseLabel(_ phase: VSDKDimensioningPhase) -> String {
        switch phase {
        case .idle:                return "Idle"
        case .initializing:        return "Warming up..."
        case .scanning:            return "Point at a box"
        case .detected(let count): return "Detected \(count) box(es)"
        case .capturing:           return "Hold still..."
        case .captured:            return "Captured"
        }
    }
}

      

Phase lifecycle

session.phase walks through:

        idle  ->  initializing  ->  scanning  ->  detected(trackCount:)  ->  capturing  ->  captured(VSDKDimensioningMeasurement)

      

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:

Field Type Default Notes
mode VSDKDimensioningMode .offline See "Offline vs Online" below.
measurementUnit UnitLength .centimeters Any Foundation.UnitLength. Pass .inches, .millimeters, etc.
maximumTrackCount Int 5 Cap on simultaneously tracked boxes. Lower values reduce GPU load on older devices (helpful on iPhone 12 Pro / 13 Pro).

Construct it explicitly when you need anything other than centimeters from UIKit:

        let config = VSDKDimensioningConfiguration(
    mode: .online,
    measurementUnit: .inches,
    maximumTrackCount: 1   // single-box workflows: keep the capture deterministic
)

      

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.

.offline (default) .online
Network required No Yes
VSDKConstants.apiKey required No Yes (throws .missingCredentials if unset)
measurement.usedCloudSAM false true

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.

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 == .detected and track.isStable from 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):

        public final class VSDKDimensioningMeasurement: NSObject {
    public let id: UUID
    public let timestamp: Date

    // Unit matches `configuration.measurementUnit`
    public let length: NSMeasurement
    public let width:  NSMeasurement
    public let height: NSMeasurement

    public let distanceFromCamera: NSMeasurement   // always meters
    public let confidence: Float                    // 0...1
    public let usedCloudSAM: Bool                   // true if .online ran

    public var volume: NSMeasurement { get }        // cubic meters, derived
}

      

Reading values

        // Same unit as your configuration's measurementUnit (cm by default)
let lengthCm = measurement.length.doubleValue

// Convert on the fly
let widthIn  = measurement.width
    .converting(to: .inches)
    .doubleValue

// Volume is always cubic meters; convert as needed
let volCm3 = measurement.volume
    .converting(to: .cubicCentimeters)
    .doubleValue

      

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:

        public final class VSDKDimensioningTrack: NSObject {
    public let id: UUID
    public let measurement: VSDKDimensioningMeasurement?   // nil until stable
    public let isStable: Bool
    public let normalizedScreenRect: CGRect                // [0,1] x [0,1]
}

      

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.

Case Code Trigger What to do
.missingCredentials 0 .online mode started without VSDKConstants.apiKey Set the key before starting, or fall back to .offline.
.notConfigured 1 startRunning() called before configure(...) Call configure(...) first.
.lidarUnavailable 2 Non-LiDAR device or simulator Hide the dimensioning entry-point; gate via deviceCapabilities().lidar upstream.
.arSessionFailed(reason:) 3 ARKit failure (camera denied, app backgrounded mid-session, hardware fault) Surface the underlying reason to the user. Camera-permission denial requires Settings.
.noGroundPlane 4 Could not anchor a horizontal floor plane Ask the user to point the camera at the floor for a moment before aiming at the box.
.captureTimedOut 5 capture() never reached a stable measurement Common when the box is too far, partially out of frame, or moving. Retry with better framing.
.userCancelled 6 Cancellation propagated from the session Treat as a soft cancel, not an error.

Example: handling the full error set

        do {
    let m = try await session.capture()
    save(m)
} catch let error as VSDKDimensioningError {
    switch error {
    case .missingCredentials:
        showAlert("Please configure your API key.")
    case .lidarUnavailable:
        showAlert("This device doesn't support dimensioning.")
    case .arSessionFailed(let reason):
        showAlert("Camera error: \(reason)")
    case .noGroundPlane:
        showHint("Point the camera at the floor first.")
    case .captureTimedOut:
        showHint("Move closer or center the box in the frame.")
    case .userCancelled:
        break   // soft cancel
    case .notConfigured:
        assertionFailure("Logic error: capture() called before start()")
    }
}

      

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 in deinit).

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:

        @State private var showScanner = true
@State private var showDimensioning = false

var body: some View {
    ZStack {
        if showScanner { ScannerView() }
        if showDimensioning { DimensioningSessionView() }
    }
    .onChange(of: showDimensioning) { newValue in
        // Mutually exclusive: ScannerView teardown happens via `showScanner = false`
        if newValue { showScanner = false }
    }
}

      

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:

  1. 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.
  2. Ensure all four top corners of the box are in frame.
  3. Avoid extreme lighting (direct sunlight on glossy tape; near-dark warehouses).
  4. Move the device into the 40-90 cm sweet spot. Very close (<30 cm) or very far (>1.5 m) captures degrade meaningfully.
  5. 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)).