◉RORK LABJP
●GPT-6.1 SOL — Added to Rork's model menu (09-29) with Low, Medium and High effort; included on Pro and Max●SONNET 5.5 — Claude Sonnet 5.5 is selectable: strong at UI design and faster than Sonnet 5 (09-28)●SDK 58 — Expo SDK 58 is out of beta as of 10/15 (beta since 09/15). Check your dependencies before upgrading●WIDGET — A write-up traces an iOS widget that renders black only in production builds●OBSERVE — EAS Observe now records native crashes too (SDK 57.0.21 or later, 10/07)●NEW — My App Open Ad Only Showed When the App…●GPT-6.1 SOL — Added to Rork's model menu (09-29) with Low, Medium and High effort; included on Pro and Max●SONNET 5.5 — Claude Sonnet 5.5 is selectable: strong at UI design and faster than Sonnet 5 (09-28)●SDK 58 — Expo SDK 58 is out of beta as of 10/15 (beta since 09/15). Check your dependencies before upgrading●WIDGET — A write-up traces an iOS widget that renders black only in production builds●OBSERVE — EAS Observe now records native crashes too (SDK 57.0.21 or later, 10/07)●NEW — My App Open Ad Only Showed When the App…
Articles/Getting Started
◈ Getting Started/2026-09-04Beginner

A Screen That Runs in Rork Companion Is Not Yet a Finished Screen

There is a gap between the app you hold in your hand through Rork Companion and the build that eventually reaches the App Store. Here is why free signing expires after a few days, what Companion can and cannot answer for you, and a small badge that keeps you honest about which build you are looking at.

Rork Companion10Device Testing8Expo222iOS116Release5

I had just added a new settings screen to one of my wallpaper apps. I sent it to my iPhone through Rork Companion, touched it, scrolled it, and everything landed where I expected. I said "done" out loud that night.

The gap showed up a week later. In the build I assembled for distribution, the same screen looked identical down to the pixel, but the purchase flow gave me nothing back. It had been running on a real device, that much was true. What had been running was a development build, not the build a user would actually receive.

I want to say this early: none of that is a shortcoming in Companion. Companion did its job. I was the one who counted a narrow check as a wide one.

Three steps to get it on your phone, and the third one is where people stop

The sequence itself is short.

  1. Open Rork's install page in Safari on your iPhone
  2. Install Companion from the link or QR code shown there
  3. Go to Settings, then General, then VPN & Device Management, and trust the developer profile

Almost everyone gets stuck on the third step. "Untrusted Developer" appears and the app refuses to launch. What helps here is a reframe: that message is not a failure, it is iOS doing its job correctly. Anything that arrives outside the store is not allowed to run until the person holding the phone says so explicitly.

There is a second thing worth knowing. A development profile signed with a free Apple ID has a short life, usually a few days to about a week. An app that opened yesterday and refuses to open this morning is not broken; its signature simply expired. Installing it again the same way brings it back.

I have come to treat that reinstall as a small inventory check rather than a chore. Each time, I have to say out loud what I am actually trying to verify.

What Companion does show you

Putting an app on a real device surfaces more than I used to assume.

What you can seeWhy the browser hides it
Real touch targetsA mouse pointer is a point; a fingertip is an area
Safe areas and notchesPer-device measurements do not survive a resized frame
Effect of the system font sizePlenty of people run their phone with larger text
Inputs hidden by the keyboardReal keyboard height and its animation delay both matter
Latency on a real connectionYour own fast connection hides the slow-network experience

Clearing these in Companion makes everything downstream easier. Most of my layout adjustments never leave this stage.

What Companion cannot answer

Some questions stay open no matter how well the app behaves in Companion. The purchase flow I missed lives in this list.

What stays unverifiedWhere to verify it
Real purchases and subscription renewalsAn internal testing track or a TestFlight build
Speed and memory in a release buildBundling and optimization differ, so measure on the distributed build
Review-facing copy, screenshots, privacy entriesThe submission forms and the artifacts you actually upload
The update channel used for OTA deliveryA build assembled with that channel set
Extension targets such as widgetsA distributed build signed together with the extension

Push notifications deserve a line of their own. A development build talks to a different delivery environment than a store build, so a notification that arrives perfectly during testing tells you very little about the one your users will receive. I now treat "a notification arrived" and "a notification arrived through the production path" as two separate checkmarks on two separate days.

Written down like this it reads as obvious. In the middle of the work, though, the boundary dissolves. When something is moving in your hand, suspecting that it might not be is genuinely hard.

"Does it run" is a question Companion can answer. "Can I ship it" is a question only a distribution build can answer. Since I put that line at the top of my working notes, I have not repeated the same miss.

Put the current build on the screen

If the boundary dissolves, I decided to mark it. I keep a small component that prints build information in the corner, but only during development and internal distribution.

// components/BuildBadge.tsx
import { Text, View } from 'react-native';
import Constants from 'expo-constants';
import * as Application from 'expo-application';
import * as Updates from 'expo-updates';
 
export function BuildBadge() {
  const forced = Constants.expoConfig?.extra?.showBuildBadge === true;
  if (!__DEV__ && !forced) return null;
 
  const lines = [
    `env: ${Constants.executionEnvironment}`,
    `id: ${Application.applicationId ?? '-'}`,
    `ver: ${Application.nativeApplicationVersion ?? '-'} (${Application.nativeBuildVersion ?? '-'})`,
    `channel: ${Updates.channel ?? '(none)'}`,
    `embedded: ${String(Updates.isEmbeddedLaunch)}`,
  ];
 
  return (
    <View
      pointerEvents="none"
      style={{ position: 'absolute', right: 8, bottom: 8, padding: 6, borderRadius: 6, backgroundColor: 'rgba(0,0,0,0.6)' }}
    >
      {lines.map((line) => (
        <Text key={line} style={{ color: '#fff', fontSize: 10 }}>{line}</Text>
      ))}
    </View>
  );
}

A quick reading guide. Constants.executionEnvironment tells you whether you are running through a store-distributed client or a build you assembled yourself. Updates.channel shows which channel the build was configured to receive updates from, and it stays empty during development. When Updates.isEmbeddedLaunch is true, you are running the code bundled into the binary and have not yet received an OTA update.

I keep the switch itself in configuration.

// app.config.ts
export default ({ config }) => ({
  ...config,
  extra: {
    ...config.extra,
    showBuildBadge: process.env.SHOW_BUILD_BADGE === '1',
  },
});

With that in place, passing SHOW_BUILD_BADGE=1 while assembling an internal build shows the badge, and the production build drops it without me remembering to.

The badge earns its keep in one more place: bug reports. I ask testers to include that corner text in whatever they send me — a screenshot is enough, since the badge is already in the frame. Before that, a report would say "the purchase button did nothing" and I would spend an evening reproducing it against the wrong build. Now the first line of every report answers that question for me.

For a while I gated this on __DEV__ alone. The badge then disappeared in internal builds, and bug reports from testers stopped telling me which build they were describing. Splitting the condition into two axes came out of that specific frustration, not out of a design principle I read somewhere.

The order I follow now

These days I move through three stages. The point of the stages is as much about what I refuse to look at as what I check.

StageWhat I check hereWhat I deliberately skip here
CompanionLayout, feel, latency on a real connectionPurchases, production push delivery, launch speed
Internal distributionPurchase flow, update channel, extension targetsFine spacing adjustments
Staged rolloutStability in real users' environmentsAdding new features

What surprised me is how much the third column changed my pace. Deciding in advance not to look at spacing during internal distribution sounds like carelessness, and for a while it felt that way. In practice it stopped me from re-opening a screen I had already settled, and it kept the internal round short enough that testers were still willing to reply. A stage that tries to check everything ends up checking nothing carefully.

Separating the stages narrows the search space when something breaks. For how I handle releases after that point, I wrote up the details in staged rollout and hotfix strategy. If the build itself is not completing yet, the build failure checklist will get you further, faster.

If you are weighing how far the free tier takes you before paying for a plan, my hands-on comparison of the pricing plans may give you something concrete to decide with.

One thing to do today

If you have an app running through Companion right now, start by adding the build badge alone. It takes about five minutes, and from then on the corner of your screen answers the question "which build am I actually looking at" without you having to guess.

I spent a long time guessing before I did this. Thank you for reading — I hope it saves you the detour it cost me.

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 →

If you found this article helpful, a small tip ($1.50) would mean a lot to us. Your support helps keep this site ad-free and covers server and hosting costs.

Related Articles

◈ Getting Started2026-09-11
Same Wi-Fi, No Preview: Where I Look Before Rebooting the Router
When Rork Companion scans the QR code and nothing arrives, there are places worth checking before you restart anything. Four settings on the iPhone, two on the router, and a one-minute test that cuts the search space in half.
◈ Getting Started2026-09-08
Whether You Need Rork Max Is Answered by Your Permissions, Not the Price Page
When I cannot decide between standard Rork and Rork Max, I stop reading the price page. Instead I pull every OS capability my app actually asks for out of app.json and Info.plist, then sort them into what Expo covers, what a config plugin covers, and what truly needs native Swift.
◈ Getting Started2026-04-09
Rork Companion: Test Your iPhone App on a Real Device — No Apple Developer Account Needed
With Rork Companion, you can test your Rork-built iOS app on a real iPhone for free — no Apple Developer account required. This guide covers scanning the QR code, troubleshooting connection issues, what to check on a real device, and what Companion can't test.