RORK LABJP
MAX — Rork Max generates native Swift for iPhone, iPad, Apple Watch, Apple TV, and Vision Pro, with 2-click App Store publishing and no Xcode requiredSTACK — Standard Rork builds cross-platform mobile apps with React Native (Expo); choosing between the two by use case is the key decisionFOCUS — Unlike web-first tools such as Bolt or Lovable, Rork specializes in native iOS and Android app generationBUGS — A hands-on review reports Rork resolved about 70% of bugs without manual help, with the remaining 30% needing edits in the exported codebaseFUNDING — Rork raised $2.8M from a16z (Andreessen Horowitz)PRICING — It is free to start, with paid plans from $25/month, so you can try before committingMAX — Rork Max generates native Swift for iPhone, iPad, Apple Watch, Apple TV, and Vision Pro, with 2-click App Store publishing and no Xcode requiredSTACK — Standard Rork builds cross-platform mobile apps with React Native (Expo); choosing between the two by use case is the key decisionFOCUS — Unlike web-first tools such as Bolt or Lovable, Rork specializes in native iOS and Android app generationBUGS — A hands-on review reports Rork resolved about 70% of bugs without manual help, with the remaining 30% needing edits in the exported codebaseFUNDING — Rork raised $2.8M from a16z (Andreessen Horowitz)PRICING — It is free to start, with paid plans from $25/month, so you can try before committing
Articles/AI Models
AI Models/2026-06-15Advanced

Building a Room-Measuring App with Rork Max and RoomPlan — Absorbing Scan Variance by Design

Now that Rork Max generates native Swift, a LiDAR-based RoomPlan scanning app is a realistic project for an indie developer. Scan results drift depending on device and how you hold it. This walks through how to absorb that drift in your app design and export stable dimensions.

Rork Max166RoomPlanLiDAR2Swift25Architecture10

Premium Article

The first time I used RoomPlan, I scanned the same small room three times and got three different wall lengths: 3.62m, 3.57m, 3.64m. A few centimeters of difference, but if you are building an app that draws a floor plan, you cannot expose that drift to the user. People do not trust a tool whose numbers change every time they measure.

Now that Rork Max can generate native Swift, a LiDAR-based measuring app suddenly came within reach for indie developers. But ship the generated code as-is and that drift lands directly in the user's hands. Here I share how I learned to absorb scan variance by design, working through a small measuring app of my own.

Why RoomPlan numbers drift

RoomPlan sits on top of ARKit. It combines the LiDAR point cloud with planes estimated from the camera image to reconstruct the room's structure. In other words, the final dimensions are not a measurement but an estimate. Because it is an estimate, the result moves with the input conditions.

There are three main sources of drift. Lighting: against backlight or in the dark, feature points are scarce and plane estimation gets loose. Motion speed: sweep too fast and the point cloud thins out, delaying how edges are resolved. And the device itself: iPhone and iPad Pro differ in LiDAR resolution and field of view, so the same room reconstructs slightly differently.

The important thing is to not try to crush these as bugs. Estimation drift is the spec, and designing your app to absorb it produces a more stable result in the end.

A three-layer design that absorbs drift

I treat measurement data in three layers: raw, confirmed, and display.

The raw layer keeps the CapturedRoom that RoomPlan returns, untouched. Nothing is rounded here. The confirmed layer applies rounding and snapping to turn raw data into "dimensions the app takes responsibility for." The display layer shows the user only the confirmed values.

With this separation, when you later want to change the rounding rules, you can redo it without throwing away the raw data. Scanning is an effort for the user, so a design that reduces re-scans directly improves the experience.

Rounding and snapping in the confirmed layer

The confirmed layer has two core operations. One is rounding to 5cm units. The other is snapping toward right angles and parallels. Real room walls are made almost entirely of right angles and parallels, so if the estimate returns 88 or 92 degrees, nudging it to 90 produces a more natural drawing.

import RoomPlan
import simd
 
struct ConfirmedDimension {
    let widthMeters: Double
    let lengthMeters: Double
    let cornerAngles: [Double]   // each corner angle, in degrees
}
 
enum DimensionResolver {
    // Round to 5cm so users see a granularity they can trust
    static func snapLength(_ raw: Double) -> Double {
        let grid = 0.05
        return (raw / grid).rounded() * grid
    }
 
    // Snap toward right angles. Within +/-4 degrees, pull to a multiple of 90
    static func snapAngle(_ rawDeg: Double) -> Double {
        let nearest = (rawDeg / 90.0).rounded() * 90.0
        return abs(rawDeg - nearest) <= 4.0 ? nearest : rawDeg
    }
 
    static func resolve(from room: CapturedRoom) -> ConfirmedDimension {
        let walls = room.walls
        let lengths = walls.map { Double($0.dimensions.x) }.sorted(by: >)
        let width = snapLength(lengths.first ?? 0)
        let length = snapLength(lengths.dropFirst().first ?? 0)
 
        let angles = walls.map { wall -> Double in
            let yaw = atan2(wall.transform.columns.0.z, wall.transform.columns.0.x)
            return snapAngle(yaw * 180 / .pi)
        }
        return ConfirmedDimension(widthMeters: width, lengthMeters: length, cornerAngles: angles)
    }
}

The +/-4 degrees threshold is a value I settled on after scanning a few times on my own devices. Snap across too wide an angle and rooms with genuinely diagonal walls break. Too narrow and you fail to absorb the drift. Carry this as a number you will tune for the kinds of rooms your app handles.

Carry a confidence value alongside

If you hand over only the rounded numbers, the display layer cannot judge how much to trust them. I attach a confidence score to the confirmed value. Even a rough calculation from point-cloud density and scan time is enough to drive what the display shows.

struct DimensionWithConfidence {
    let dimension: ConfirmedDimension
    let confidence: Double   // 0.0 to 1.0
 
    var needsRescanHint: Bool { confidence < 0.6 }
}

When needsRescanHint is set, the display layer adds "scanning again slowly will improve accuracy." Rather than hiding the numbers, honestly conveying their certainty is what earns the user's trust in the tool.

Thank you for reading this far.

Continue Reading

What follows includes implementation code, benchmarks, and practical content we hope you'll find useful. This site runs without ads — server and development costs are supported entirely by members like you. If it's been helpful, we'd be truly grateful for your support.

WHAT YOU'LL LEARN
A rounding and snapping design that assumes RoomPlan results drift 5-10cm by device, lighting, and hand motion, so you can lock in trustworthy dimensions
How to add a minimal shared-contract layer that exports dimensions to JSON on top of the Swift code Rork Max generates
Judgment criteria for a LiDAR-absent fallback that survives App Store review instead of getting rejected
Secure payment via Stripe · Cancel anytime

Unlock This Article

Get full access to the rest of this article. Buy once, read anytime. This site is ad-free — your support goes directly toward keeping it running.

or
Unlock all articles with Membership →
Share

Thank You for Reading

Rork Lab is ad-free, supported entirely by members like you. We publish practical guides daily with implementation code, benchmarks, and production-ready patterns. If you've found it useful, we'd love to have you on board.

  • Copy-paste ready implementation code
  • New advanced guides published daily
  • $5/mo or $10 for lifetime access
View Membership →

Related Articles

AI Models2026-03-10
How Claude Opus 4.6 Powers Rork Max's Development Engine
Discover how Claude Opus 4.6 and Anthropic's AI engine power Rork Max to generate native Swift apps in minutes.
Dev Tools2026-06-16
Designing CloudKit Sync in a Rork Max Native App — Handling Conflicts and Deletes
You want the same data on iPhone and iPad. When you add CloudKit to a Swift app generated by Rork Max, the hard part is not saving — it is conflicts and deletes. Here are the design decisions I settled on.
Dev Tools2026-06-15
Drawing the Line Between Rork Max's Swift Output and the Expo Build
Rork Max now generates native Swift, while the standard Rork keeps producing Expo (React Native) apps. Here is how to split responsibilities between the two engines inside a single app business, viewed from real maintenance cost.
📚RECOMMENDED BOOKS
Build a Large Language Model (From Scratch)
Sebastian Raschka
LLM Dev
Prompt Engineering for LLMs
Berryman & Ziegler
Prompting
AI Engineering
Chip Huyen
AI Eng
* Contains affiliate links
See all →