●BUILD — Rork Max runs real Macs in the cloud loaded with Xcode and the iOS SDK, writing SwiftUI, compiling, reading the errors and building again. That loop, not the code generation, is what lifts the output●NATIVE — What comes out is pure Swift and SwiftUI, not React Native. Reaching AR, Metal graphics and widgets that React Native cannot touch is the real gap between this and other builders●PLATFORMS — Coverage spans iPhone, iPad, Apple Watch, Apple TV and Vision Pro, plus iMessage. Worth a look if you want to start from a watch app or an extension rather than a phone screen●COMPANION — The Rork Companion app lets you check a generated build on a real iPhone without a paid Apple Developer account, lowering the bar for trying a first project end to end●PRICING — Free to start, paid plans from $25 a month, and Rork Max on the $200 Max plan. Worth working out up front how many projects it takes to earn that back●DEADLINE — From August 31, 2026, Google Play requires target API level 36 or higher for new apps and updates alike. Ten days out, and the targetSdkVersion of what you generate is yours to verify●BUILD — Rork Max runs real Macs in the cloud loaded with Xcode and the iOS SDK, writing SwiftUI, compiling, reading the errors and building again. That loop, not the code generation, is what lifts the output●NATIVE — What comes out is pure Swift and SwiftUI, not React Native. Reaching AR, Metal graphics and widgets that React Native cannot touch is the real gap between this and other builders●PLATFORMS — Coverage spans iPhone, iPad, Apple Watch, Apple TV and Vision Pro, plus iMessage. Worth a look if you want to start from a watch app or an extension rather than a phone screen●COMPANION — The Rork Companion app lets you check a generated build on a real iPhone without a paid Apple Developer account, lowering the bar for trying a first project end to end●PRICING — Free to start, paid plans from $25 a month, and Rork Max on the $200 Max plan. Worth working out up front how many projects it takes to earn that back●DEADLINE — From August 31, 2026, Google Play requires target API level 36 or higher for new apps and updates alike. Ten days out, and the targetSdkVersion of what you generate is yours to verify
Four Countries, Not the World: Checking Which of My Six Apps the September 30 Deadline Actually Touches
Android developer verification goes live on September 30, 2026 in Brazil, Indonesia, Singapore, and Thailand. Here's how to decide from your own install data whether that date is urgent for you, and what actually blocks indie developers.
My notes said "fully mandatory in September 2026." That was about Android developer verification. September is next month, and two of the apps I run as an indie developer are distributed on Google Play. So I went back and reread the official pages — and what I found there was a list of four country names.
Brazil, Indonesia, Singapore, Thailand.
What begins on September 30, 2026 is enforcement in those four countries, not worldwide. Global expansion is explicitly listed as 2027. Had I spent August believing "everything stops in September," I would have gotten my priorities badly wrong.
That said, whether four countries is small depends entirely on who you are. In categories where the app barely depends on language — wallpapers being the clearest example — traffic from emerging markets runs higher than most people expect. "Only four countries" is not reassuring until you've looked at your own numbers. This is a record of how I checked mine.
The dates that are actually confirmed, and the stores involved
Let me start with what is officially settled. To keep speculation out of it, the dates and country names below come only from what's published in the Android Developer Console help pages.
When
What happens
November 2025
Early access registrants began verifying apps they distribute outside Google Play
March 2026
The full Android Developer Console experience opened to all developers
September 30, 2026
In Brazil, Indonesia, Singapore, and Thailand, only apps registered by verified developers can be newly installed from the participating stores
2027
Expansion to all apps distributed to certified Android devices, globally
The participating stores for this first phase are spelled out too. This is the part people skim past.
Company
Store
Google
Google Play
Honor
HONOR App Market
OPlus
OPPO App Market
Samsung
Galaxy Store
Transsion
Palm Store
vivo
V-Appstore
Xiaomi
GetApps
Google Play sits at the top of that list. I had initially filed this whole thing under "sideloading regulation" — something aimed at people who hand out APKs directly, and therefore not really my problem since I ship through Play. It's the opposite. Installs through Play are covered. "I only ship on Play, so this doesn't apply to me" is the single most dangerous misreading here.
Sideloading itself isn't going away either. Apps from unverified developers remain installable through an advanced flow, after the user acknowledges the risk and completes a one-time setup, and ADB continues to work as the normal path for development. But those are escape hatches for someone holding the device. They are not a distribution strategy.
Deciding whether "four countries" is small for you
Here's the part that actually matters. Once the scope is fixed at four countries, you only need one number: what share of your installs comes from them. If it's one percent, September 30 isn't an emergency. If it's twenty percent, you want to move in August.
Export the country-level install report from Play Console's statistics section and aggregate it. One thing trips me up every single time: the CSV reports Play Console hands you are UTF-16LE with a BOM. Read them as UTF-8 and the column matching fails. If you get an error saying a column can't be found, suspect the encoding before anything else.
// scripts/enforcement-share.mjs// Aggregates the country-level install CSV exported from Play Console > Statistics.// Usage: node scripts/enforcement-share.mjs installs_country_202607.csvimport fs from "node:fs";// The four countries where enforcement starts on 2026-09-30 (ISO 3166-1 alpha-2)const ENFORCED = new Map([ ["BR", "Brazil"], ["ID", "Indonesia"], ["SG", "Singapore"], ["TH", "Thailand"],]);const file = process.argv[2];if (!file) { console.error("Usage: node scripts/enforcement-share.mjs <country install CSV>"); process.exit(1);}// Play Console reports are UTF-16LE with a BOM. Reading them as utf8 breaks the header row.const raw = fs.readFileSync(file, "utf16le").replace(/^/, "");const lines = raw.trim().split(/\r?\n/);if (lines.length < 2) { console.error("No readable rows. Check that the file is UTF-16LE."); process.exit(1);}const cols = lines[0].split(",").map((c) => c.trim());const iCountry = cols.findIndex((c) => /country/i.test(c));// Several install-related metrics ship in the same export; prefer the device-level one.const iInstalls = cols.findIndex((c) => /install/i.test(c) && /device|unique/i.test(c));const iFallback = cols.findIndex((c) => /install/i.test(c));const target = iInstalls >= 0 ? iInstalls : iFallback;if (iCountry < 0 || target < 0) { console.error("Could not identify the columns. Actual header:", cols.join(" | ")); process.exit(1);}let total = 0;const hits = new Map();for (const line of lines.slice(1)) { const cells = line.split(","); const code = (cells[iCountry] ?? "").trim().toUpperCase(); const n = Number((cells[target] ?? "0").replace(/[^\d.-]/g, "")) || 0; if (!code || n <= 0) continue; total += n; if (ENFORCED.has(code)) hits.set(code, (hits.get(code) ?? 0) + n);}const affected = [...hits.values()].reduce((a, b) => a + b, 0);const share = total > 0 ? (affected / total) * 100 : 0;console.log(`Installs counted: ${total.toLocaleString()}`);for (const [code, label] of ENFORCED) { const n = hits.get(code) ?? 0; const pct = total > 0 ? ((n / total) * 100).toFixed(2) : "0.00"; console.log(` ${label} (${code}): ${n.toLocaleString()} (${pct}%)`);}console.log(`Four-country total: ${affected.toLocaleString()} (${share.toFixed(2)}%)`);console.log( share >= 5 ? "-> Start in August. That leaves room for a paperwork round trip." : "-> September 30 is not urgent. Getting ready before the 2027 global rollout is enough.");
The output looks like this:
Installs counted: 412,880
Brazil (BR): 21,043 (5.10%)
Indonesia (ID): 38,512 (9.33%)
Singapore (SG): 1,204 (0.29%)
Thailand (TH): 9,870 (2.39%)
Four-country total: 70,629 (17.11%)
-> Start in August. That leaves room for a paperwork round trip.
The five percent threshold is my own call, not a rule. For a solo developer, the registration work itself takes hours at most. If a few hours of paperwork protects more than five percent of your distribution, there's no reason to defer it. Your own cutoff will depend on your scale — look at the number first, then decide.
One thing becomes obvious once you run this per app: the ratio varies enormously between apps from the same developer. An app whose value is carried by Japanese text and an app that barely uses text at all end up with completely different emerging-market profiles. Among the apps I run, the ones with the least on-screen text consistently show the highest share from Southeast Asia. "Our app is domestic anyway" is a feeling, not a measurement — and it has to be checked per app.
✦
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
✦You will be able to decide in about thirty minutes, from your own country install split, whether the September 30 date is urgent for your app or not
✦You will avoid discovering the real bottleneck (a registration number that takes weeks to issue) only after the deadline has passed
✦You will be able to rank several apps by which to register first, using numbers instead of guesswork
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.
The bottleneck isn't the deadline. It's the wait in front of it.
After confirming the timeline I nearly relaxed, and then noticed something else. If an indie developer genuinely misses this, the cause won't be September 30. It'll be the waiting period that sits in front of it.
Registering as an organization requires a D-U-N-S number. If you don't have one you can get it for free, but the official guidance says issuance can take up to 28 days.
Today is August 11. Add 28 days and you land on September 8, which leaves 22 days of margin before September 30 — enough to lose entirely to a single round trip over an incomplete form. Spend August without deciding whether you're registering as an individual or as an organization, and one of those two options quietly disappears. If the naming decision itself is what you're stuck on, the framing in Will your real name appear on the App Store? covers the tradeoffs.
The account types are worth laying out plainly:
Type
Fee
Distribution
ID check
Full distribution
$25
No limit
Government ID required
Limited distribution
Free
Unlimited apps, up to 20 devices
No government ID required
Limited distribution is aimed at students, hobbyists, and people sharing with a closed circle. The 20-device ceiling means it can't carry an app you publish to a store. It is, however, a realistic home for the test packages you hand around separately from your production apps.
The unit of the audit turned out to be apps, not accounts
If verification stopped at confirming who the developer is, this would be a one-time task. But app registration is part of it, which means the unit you have to inventory is not the developer account — it's every application identifier you actually distribute.
That turned out to be messier than I expected. After running apps for years, what sits alongside your live products includes test packages, older versions republished under a different name, and entries that got split out by form factor. Scrolling the Play Console list doesn't tell you which of those are live and which are residue.
If you have several Expo or Rork projects, pulling the identifiers out of the repositories is faster than clicking through consoles.
// scripts/inventory-identifiers.mjs// Collects applicationId and bundleIdentifier across several Expo / Rork projects.// Usage: node scripts/inventory-identifiers.mjs ~/projects/*import fs from "node:fs";import path from "node:path";const roots = process.argv.slice(2);if (roots.length === 0) { console.error("Usage: node scripts/inventory-identifiers.mjs <project dirs...>"); process.exit(1);}const rows = [];for (const root of roots) { if (!fs.existsSync(root) || !fs.statSync(root).isDirectory()) continue; // Handle either app.json or app.config.json const candidates = ["app.json", "app.config.json"].map((f) => path.join(root, f)); const configPath = candidates.find((p) => fs.existsSync(p)); if (!configPath) { // app.config.js needs evaluation, so flag it explicitly rather than skipping silently if (fs.existsSync(path.join(root, "app.config.js"))) { rows.push({ project: path.basename(root), android: "manual (app.config.js)", ios: "manual", channel: "-" }); } continue; } const cfg = JSON.parse(fs.readFileSync(configPath, "utf8")); const expo = cfg.expo ?? cfg; // If eas.json exists, note which profile actually ships to production let channel = "-"; const easPath = path.join(root, "eas.json"); if (fs.existsSync(easPath)) { const eas = JSON.parse(fs.readFileSync(easPath, "utf8")); channel = eas?.build?.production?.channel ?? "production (no channel set)"; } rows.push({ project: path.basename(root), android: expo?.android?.package ?? "NOT SET", ios: expo?.ios?.bundleIdentifier ?? "NOT SET", channel, });}if (rows.length === 0) { console.error("No Expo config found. Check the paths you passed."); process.exit(1);}console.table(rows);const missing = rows.filter((r) => r.android === "NOT SET");if (missing.length > 0) { console.warn( `\n${missing.length} project(s) have no explicit android.package.` + "\nThey may be shipping under an identifier generated at build time — reconcile against Play Console." );}
Any project that comes back without an explicit android.package deserves your attention first. An identifier that isn't written down in the config usually isn't in the developer's memory either, and those are exactly the entries that fall off an inventory.
Check who holds the signing key while you're in there
Once the identifiers are listed, add one more column: which key each app is signed with. It has nothing to do with verification registration directly. It's just that this is the only occasion you'll touch that information all year.
The command to inspect a built artifact differs between APK and AAB, and this is easy to get wrong.
# APK: apksigner prints the certificate fingerprintsapksigner verify --print-certs app-release.apk | grep -i "SHA-256 digest"# AAB: apksigner cannot verify an app bundle. Use keytool instead.keytool -printcert -jarfile app-release.aab | grep -i "SHA256:"# If you use Play App Signing, what you see here is the UPLOAD key.# The key that signs what reaches devices is held by Google — compare against# Play Console > Setup > App signing.
Expected output:
SHA-256 digest: 3f:aa:19:...:c2:70
Passing an AAB to apksigner verify fails with a format error. I once spent real time convinced a signature had broken, when the tool simply doesn't accept that input. If you've ever had an update blocked by something in the signing chain, the expiry monitoring I described in The update that failed because a profile expired pairs well with this audit.
Three things that ran opposite to my expectations
Coming out of this, three findings contradicted what I had assumed going in.
First, the blast radius was smaller than I thought. Under "fully mandatory in September," August becomes an all-hands month across every app. In reality it's four countries, with global expansion in 2027. Whether you need to hurry is settled by one aggregation of your own country split.
Second, and pulling the other way: Google Play is in scope. The policy gets discussed almost entirely in sideloading terms, yet Play heads the list of participating stores in the first phase. "I only ship on Play" does not exempt you.
Third — and this is the one I didn't see coming — the rate limiter is not the deadline but the issuance wait for a business registration number. There is essentially no engineering work here. Not one line of code changes. And yet the way you'd miss it is a paperwork round trip. Recognizing it as a deadline that engineering cannot rescue is what moved it up my priority list.
The sequence to run before the deadline
Once you have the number, the order goes like this.
Export the country-level install CSV from Play Console and run it through the script above. If the four-country share lands under five percent, you are done here.
Decide between an individual and an organization registration. If you choose organization, start the D-U-N-S request first — it can take up to 28 days.
Collect android.package values from your repositories and reconcile them against the apps listed in Play Console. Identifiers that appear on only one side are the most valuable output of this whole exercise.
Separate the apps you publish to stores from the test packages that fit within 20 devices. The latter can move to a limited distribution account.
Complete identity verification in the Android Developer Console and register the apps you settled on in step 3.
Steps 1 through 3 are read-only. The only step that's hard to walk back is step 5 — which is exactly why skipping step 1 and starting at step 5 leaves you carrying registrations you never needed.
What I decided to do in August
With the numbers in front of me, here's where I landed.
For the two apps I ship on Google Play, I'm completing Android Developer Console registration during August. The four-country share isn't small enough to ignore, and the work itself is a few hours. I'm proceeding under an individual name. Whether to move to a business entity is a decision that belongs on its own timeline, not this one — letting a deadline pick your legal identity gets the order backwards.
The packages I hand out for testing fit inside the 20-device ceiling, so those move to a limited distribution account.
One closing note. Every date, fee, and country in this article reflects what was officially published as of August 11, 2026. Details in programs like this often shift right before enforcement begins. Confirm the current terms against primary sources before you register. I nearly misjudged my own priorities because the note I'd been working from still said "fully mandatory in September."
There's only one thing to do first: run one country-level install CSV through the script above. The percentage that comes out will tell you how to spend August.
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.