RORK LABJP
IOS27 — iOS 27 and iPadOS 27 land on September 14. For apps assembled with no-code or AI tooling, the week a new OS ships is when the ground moves mostDUO — Apple's first foldable, the iPhone Duo, starts at $1,999. A new screen shape is also the first place a generated layout tends to breakSPLIT — Standard Rork produces React Native through Expo. Rork Max generates native Swift. Neither will adapt to a folding screen at the same pace, or fail in the same waySUBMIT — Automated submission absorbs every change Apple makes to the process. An article praising the convenience owes its readers that caveatRATING — Answering the age rating questionnaire became mandatory in September. Apps built without code are no exceptionSILICON — The A20 Pro in the iPhone 18 Pro is reported to be the first high-volume smartphone processor built on TSMC's 2nm nodeIOS27 — iOS 27 and iPadOS 27 land on September 14. For apps assembled with no-code or AI tooling, the week a new OS ships is when the ground moves mostDUO — Apple's first foldable, the iPhone Duo, starts at $1,999. A new screen shape is also the first place a generated layout tends to breakSPLIT — Standard Rork produces React Native through Expo. Rork Max generates native Swift. Neither will adapt to a folding screen at the same pace, or fail in the same waySUBMIT — Automated submission absorbs every change Apple makes to the process. An article praising the convenience owes its readers that caveatRATING — Answering the age rating questionnaire became mandatory in September. Apps built without code are no exceptionSILICON — The A20 Pro in the iPhone 18 Pro is reported to be the first high-volume smartphone processor built on TSMC's 2nm node
Articles/Dev Tools
Dev Tools/2026-07-27Advanced

When to Raise Your Minimum iOS Version — Count Leftover Branches, Not User Percentages

Judging a minimum OS bump by usage share produces the same answer every year, so the decision never happens. Here is the annotation convention, the sweep script that counts how many iOS and Android branches each candidate floor would retire, and what to watch for 30 days after.

Rork559Expo205React Native237Maintenance3iOS114Long-term Operations4

Premium Article

Last autumn I opened the same spreadsheet I had opened the two autumns before it.

I pasted in the session share by OS version from App Store Connect and looked at the oldest row. 0.9 percent. The year before it was 1.4, and before that 2.1. The number was shrinking on schedule.

The conclusion never did. "Still can't drop it," I typed, and closed the sheet.

By the third year I understood that my judgment wasn't the problem. The metric was. Usage share tells you how many people you would stop reaching. It says nothing at all about what you keep carrying if you don't.

Usage share can only argue for waiting

When you maintain several apps on your own, decisions get made in whatever minutes are left over. That's exactly why a single number is so tempting.

The trouble is that usage share only pushes in one direction.

Say your threshold is 1 percent. At 0.9 percent, can you actually pull the trigger? No — you tell yourself it'll be 0.5 next year, so wait. At 0.5 percent you say something different but equivalent: is it really worth taking on release risk to cut a mere half a percent? The denominator shrinks, the pain of cutting shrinks with it, and your internal threshold quietly shrinks right alongside both.

I run six wallpaper apps in parallel. The cost of deferring was accumulating six times over, in a place I wasn't looking.

That place is the branches that paper over OS generation differences.

// A real example, simplified
if (Number.parseInt(String(Platform.Version), 10) >= 17) {
  await Sharing.shareAsync(uri, { UTI: 'public.png' });
} else {
  // Older OS closes the multi-select share sheet halfway through, so send one at a time
  for (const one of uris) {
    await Sharing.shareAsync(one, { UTI: 'public.png' });
  }
}

That branch was correct the day it was written. The problem is that nowhere in the codebase does it say when it becomes safe to delete.

Six months later, I can't tell whether the else path is still load-bearing. Because I can't tell, I don't delete it. Because I don't delete it, every future change to this screen means verifying both paths.

So the real cost of deferring never showed up as lost users. It showed up as a count of branches I couldn't retire.

Which suggested an obvious move: count them directly, and let that count be the input to the decision instead of usage share.

Give the branches one place to live

The first step was deciding where OS checks are allowed to be written, and in what form.

Instead of reading Platform.Version from every screen, I wrapped it in a comparison function. Element-wise numeric comparison, so that string ordering never turns "9.0" into something greater than "10.0".

// src/lib/os.ts
import { Platform } from 'react-native';
 
const parse = (v: string | number): number[] =>
  String(v).split('.').map((n) => Number.parseInt(n, 10) || 0);
 
/** True when the running iOS is at least `target`. Always false off iOS. */
export function atLeastIOS(target: string): boolean {
  if (Platform.OS !== 'ios') return false;
  const cur = parse(Platform.Version);
  const req = parse(target);
  for (let i = 0; i < Math.max(cur.length, req.length); i++) {
    const a = cur[i] ?? 0;
    const b = req[i] ?? 0;
    if (a !== b) return a > b;
  }
  return true;
}

I ran the comparison logic against the boundary cases before trusting it: a bare "17" with the minor omitted, "17.0.1" against "17.1", "16.4" against "16.4.1". Seven cases, all matching expectations. Version comparison looks obviously right the moment you write it and falls apart the moment a third component appears.

The floor itself lives in exactly one file.

// src/config/supportMatrix.ts
// The OS floor this app officially supports. This file is the single source of truth.
// Never change it without running os-sweep first.
export const MIN_OS = {
  ios: '16.0',
  android: 31,
} as const;

And every OS branch carries an annotation:

// @drop-when ios>=17.0 -- multi-select share sheet closes early below 17.0
if (!atLeastIOS('17.0')) {
  return <LegacyShareSheet uris={uris} />;
}

The format is deliberately trivial: @drop-when ios>=X, then --, then the reason.

What matters isn't the rigor of the syntax. It's that "when can this go" and "why does this exist" sit two characters away from the branch itself. With both present, future-you can make the call in seconds.

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
The full sweep script that reads @drop-when annotations and reports how many iOS and Android branches each candidate minimum would let you delete
A three-level exit code design that fails CI on stale branches left behind after a bump, plus the GitHub Actions wiring
Why your crash-free rate improves right after a bump, and why recording that as a win will distort next year's decision
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 $15 for lifetime access
View Membership →

Related Articles

Dev Tools2026-05-28
Tracking Down BGTaskScheduler.submit Error Code=1 (Unavailable) in Rork iOS Apps
When BGTaskScheduler.submit returns Error Code=1, the cause space is finite — six of them. Includes the identifier trap specific to Expo-based Rork apps and a getPendingTaskRequests self-check, ordered the way I actually hit them.
Dev Tools2026-04-02
Adding Native Share Functionality to Your Rork App — A Complete Share Sheet Guide
Learn how to implement native Share Sheet functionality in your Rork app. From sharing text and URLs to images and deep links, this beginner-friendly guide walks you through real code examples for iOS and Android.
Dev Tools2026-09-10
I Added a Dim Screen Button, and iPhones Stayed Dark After the App Was Closed
In expo-brightness, setBrightnessAsync applies only to the current activity on Android, but changes the device brightness itself on iOS. Here is why the cleanup burden falls on one platform only, and a hook that restores brightness through AppState.
📚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