Last night I was talking through a feature with Rork for the next update. I named a capability I had read would arrive with the new OS, and asked for it by name.
What came back looked tidy. The types lined up, the editor showed no red squiggles, and the build went through.
The capability I had asked for was nowhere in it. In its place sat the same approach I have been using since last year.
Getting an answer back and getting what you asked for are two different things. iOS 27 and iPadOS 27 are announced for September 14, so settling on a way to check now will save you a fair amount of the weeks that follow.
There are two kinds of "doesn't know"
It helps to separate them, because they behave nothing alike.
The first is inventing a name that does not exist. A class or a method appears that the SDK has never heard of, and the build stops. Your work stops too, but the stop itself is the message. This is the kind of failure I am glad to get.
The second is falling back to something it does know. When the new name is absent from what the model learned, it fills the gap with the nearest familiar shape. That shape is real code, so it compiles, and it runs. It simply does not contain the thing you asked for. This is the quiet one.
I do not think of it as dishonesty. Called into territory it has not seen, the model drifts toward the closest thing it has — that is all it is. The trouble is that the drift leaves no mark on the side that receives it.
| Kind | What happens | Where you notice | Risk |
|---|---|---|---|
| Invents a name | Build fails | Build time, editor warnings | Low — it stops you |
| Falls back to the old path | Compiles, runs, feature missing | Often not until a device | High |
| Substitutes a lookalike | Behaviour only resembles the request | Only with old and new devices side by side | High |
In the weeks around a new OS release, the second and third grow more common. The world the model learned from does not contain the feature yet.
With standard Rork, a new OS API never reaches the JS side
A quick line between the products first. Standard Rork generates React Native through Expo. Rork Max, a separate product released in February 2026, generates native Swift. Same promise on the surface, very different footing when a new OS lands.
Take standard Rork first. What JavaScript can call is limited to whatever has a bridge on the native side — an Expo module, or a community native module.
So a brand-new OS API becomes reachable only after Expo and React Native move up a version and the modules follow. Until that bridge exists, no amount of careful prompting gets you there. What the model returns instead is something writable in JavaScript that resembles the request.
You can count the bridges you actually have in a few seconds.
# List the config plugins declared in app.json
node -e "const c=require('./app.json');console.log((c.expo.plugins||[]).map(p=>Array.isArray(p)?p[0]:p).join('\n'))"
# To see what actually got applied
npx expo config --type introspectAnything absent from that output is outside your reach today. It is not a prompting problem, and a higher plan does not change it. What cannot reach you will not reach you because you asked more politely.
Holding that line in advance is what keeps you from burning an afternoon after release asking why something will not work. If you know it cannot arrive yet, waiting becomes a decision rather than a default.
With Rork Max, a clean compile only proves the name exists
Rork Max generates native Swift and compiles it on a Mac in the cloud. It can follow a new OS as soon as Xcode and the SDK do, which makes its queue one step shorter.
A shorter queue is not the same as correct code. If the model has not seen the new SDK, it drifts to the older approach here too — and because that approach is real, it compiles.
A clean compile tells you that the name exists in the SDK. It tells you nothing about whether the new path you asked for is the one being taken. Mistake one for the other and you find out after shipping.
There is a useful tell. Code that uses an API exclusive to a new OS has to carry a branch for older ones.
if #available(iOS 27, *) {
// Only devices on the new OS come through here
startWithNewCapability()
} else {
// Everything older lands here
startWithExistingCapability()
}When you want the whole function gated, the annotation goes on the declaration.
@available(iOS 27, *)
func startWithNewCapability() {
// Implementation for the new OS only
}Which gives you this: if the generated code contains no #available and no @available anywhere, yet the explanation claims a new API is in use, treat the explanation as suspect. Code that needs no branch is code that runs on older systems — the old path. You can apply that reading without knowing much Swift at all.
The order I check generated code in
Three steps, and the order matters.
One: confirm the name exists, in the body of the official documentation. Not a search summary, not a roundup post. Open Apple's Developer Documentation or the Expo documentation and look for it on the page. Secondhand write-ups are least reliable exactly when they are most abundant, which is the fortnight after a release.
Two: look for the branch. In Swift, that means #available. On the React Native side, it means asking whether the module is in your dependencies at all. If neither a branch nor a dependency is present while the text claims a new capability, stop there.
Three: put it on an older device. Checking only on a device running the new OS exercises one side of the branch. If you have no spare hardware, booting a simulator on the previous version still catches the missing half. I wrote up how I go through a published app on a beta build in Try your published Rork app on a spare iPhone before iOS 27 goes final.
Then one more thing after the three. Confirm with a single log line that the path you asked for was actually taken. A screen rendering correctly is not evidence that new code ran. I have skipped this more than once and learned it after shipping — the more correct the screen looks, the less inclined anyone is to check.
What I hold back for the two weeks from September 14
Shipping apps on my own has settled me into a rhythm for this season, and it comes down to a split.
I ship changes that stay inside paths that already exist: wording, layout adjustments, fixes I already understood. Those are largely untouched by a new OS.
I hold back anything that touches a new API, and wait for the bridge. Rushing it usually means rewriting in two weeks, and that rewrite goes through review all over again.
One exception: preparing for new screen shapes does not have to wait. When a folding form factor enters the lineup, the first thing to break is any view built on a fixed width — and you can fix that today, with the code you already have. Whether to pin the build environment itself is a separate call, and I went through it in Leave image out of eas.json and Xcode moves the day you bump your SDK.
If you do one thing tonight, run that node -e line and keep the list of bridges somewhere you can see it. When release week has you wondering whether a capability is even in reach, that list answers it in seconds.
Thank you for reading. The footing shifts for all of us in a new OS week. What I would rather keep in my own hands is the line between what I ship during the wobble and what I let wait.