●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 required●STACK — Standard Rork builds cross-platform mobile apps with React Native (Expo); choosing between the two by use case is the key decision●FOCUS — Unlike web-first tools such as Bolt or Lovable, Rork specializes in native iOS and Android app generation●BUGS — A hands-on review reports Rork resolved about 70% of bugs without manual help, with the remaining 30% needing edits in the exported codebase●FUNDING — 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●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 required●STACK — Standard Rork builds cross-platform mobile apps with React Native (Expo); choosing between the two by use case is the key decision●FOCUS — Unlike web-first tools such as Bolt or Lovable, Rork specializes in native iOS and Android app generation●BUGS — A hands-on review reports Rork resolved about 70% of bugs without manual help, with the remaining 30% needing edits in the exported codebase●FUNDING — 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
Checking Age Without Collecting Birthdays — Wiring the Declared Age Range API into a Rork App
How to use the iOS 26 Declared Age Range API to receive an age band without ever storing a birthdate, with both the Rork Max native Swift path and the standard Rork (Expo) native-module bridge, plus where to draw the responsibility boundary.
I was about to ship a small wallpaper-app update and was reviewing the age-related settings in App Store Connect. The field where declaring an "age rating" used to be enough now sat next to a new premise: in some regions, actual age verification is expected.
My first instinct was the old-fashioned one—add a birthdate field. But for a small solo-developer app, holding someone's birthdate is too heavy a burden. The moment you collect it, it becomes personal data you are responsible for storing, protecting, and deleting on request.
The Declared Age Range API, introduced in iOS 26, offers a different route. Your app asks the system for an age band rather than a birthday, and the OS answers with the minimum it needs to, based on the signed-in iCloud account. No birthdate ever reaches your app. This article walks through wiring that API into apps built with Rork, covering both the Rork Max (native Swift) and standard Rork (Expo) paths.
The Problem This API Is Solving
Traditional age checks carry a structural dilemma. Push for accuracy and you end up collecting birthdates or IDs, which inflates both privacy risk and operational cost. Settle for a single "Are you 13 or older?" checkbox and you haven't really verified anything.
The Declared Age Range API hands that bind off to the OS. All your app declares is the age gates that are meaningful to it. Pass boundaries like 13, 16, and 18, and the system asks the user, then reports only which band they fall into. Your app can learn that someone is "in the 16-and-over band" without ever receiving an exact age or birthday.
I find the trade-off in this design convincing. What an app actually needs is a basis for deciding "is this the right audience for this feature"—not the birthday itself.
✦
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 pattern that handles all three requestAgeRange outcomes (shared, declined, age band) without dropping a case
✦How to build the native-module boundary so a standard Rork (Expo) app can call the API and survive regeneration
✦A realistic read on the Texas new-account requirement (from Jan 1, 2026, iOS/iPadOS only) and how far it actually reaches
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.
Prerequisites — Capability and Platform Conditions
Before implementation, confirm the groundwork.
First, add the com.apple.developer.declared-age-range capability to your app target. Without it, the API call fails with a permissions error. If you're generating native Swift with Rork Max, check that this key is present in the generated Xcode project's entitlements.
Second, platform support. The API is available on iOS 26 / iPadOS 26 / macOS 26 and later. It cannot be called on earlier systems, which makes the fallback described below mandatory.
Third, scope. As a representative case where age verification becomes legally required, Texas imposes a verification obligation for new Apple Accounts created on or after January 1, 2026. That obligation applies to iOS / iPadOS apps and does not impose a blanket age gate on every user in every region. Get this wrong and you'll add needless friction for everyone.
Implementation in Rork Max (Native Swift)
Because Rork Max generates native Swift, you can call the API directly. In SwiftUI, the action arrives as the requestAgeRange environment value.
The code below is a minimal setup that switches a screen on "16 or older." What it solves: getting the single signal you need to gate a feature, while storing no birthdate at all.
import SwiftUIimport DeclaredAgeRangestruct AgeGatedView: View { @Environment(\.requestAgeRange) private var requestAgeRange @State private var isAdultContentAllowed = false @State private var didAsk = false var body: some View { Group { if isAdultContentAllowed { MatureFeatureView() } else { StandardFeatureView() } } .task { guard !didAsk else { return } didAsk = true await checkAgeRange() } } private func checkAgeRange() async { do { // Declare only the boundaries meaningful to your app let response = try await requestAgeRange(ageGates: 13, 16, 18) switch response { case .sharing(let range): // Compare strictly only when a lower bound is available if let lowerBound = range.lowerBound, lowerBound >= 16 { isAdultContentAllowed = true } else { isAdultContentAllowed = false } case .declinedSharing: // If sharing is declined, fail safe isAdultContentAllowed = false @unknown default: isAdultContentAllowed = false } } catch { // No entitlement, unsupported OS, etc. -> safe default isAdultContentAllowed = false } }}
The important detail here is that lowerBound can be nil. When the system can't pin down a boundary, no lower bound comes back, so you unwrap with if let before comparing. Treating nil as "does not meet the condition" and failing safe is the baseline policy.
Why pass multiple boundaries like 13, 16, and 18? Because the system reports bands along the boundaries you declare. If your app only branches at 16, declaring 16 alone works—but passing the meaningful boundaries together leaves room for future features that branch differently.
Handling "Who Declared It" — Self vs. Guardian
The age band in the response carries an attribute: whether it was self-declared by the person or declared by a guardian through Family Sharing. When you want to tailor a child-appropriate experience, that distinction matters.
case .sharing(let range): switch range.ageRangeDeclaration { case .guardianDeclared: // Age band a guardian declared via Family settings applyChildSafeDefaults() case .selfDeclared: // Age band the person declared themselves applyStandardExperience() @unknown default: applyChildSafeDefaults() }
If it's guardian-declared, it's natural to apply stronger minor-safe defaults (suppressing ad personalization, restricting outbound links, and so on). Rather than lumping self-declared and guardian-declared together, lean conservative when in doubt—it's insurance against later review and disputes.
Calling It from Standard Rork (Expo) — The Native-Module Boundary
This may be the real crux for many Rork users. Standard Rork is built on React Native (Expo), so you cannot call requestAgeRange directly from JavaScript / TypeScript. The API only exists on the Swift side.
So you slip a thin native module in between to bridge it. With the Expo Modules API, you can wrap a Swift function so JS can await it.
// modules/age-range/ios/AgeRangeModule.swiftimport ExpoModulesCoreimport DeclaredAgeRangeimport SwiftUIpublic class AgeRangeModule: Module { public func definition() -> ModuleDefinition { Name("AgeRange") // Called from JS as: await AgeRange.checkLowerBound(16) AsyncFunction("checkLowerBound") { (gate: Int) -> Bool in guard #available(iOS 26.0, *) else { return false } let service = AgeRangeService() let response = try await service.requestAgeRange(ageGates: 13, 16, 18) switch response { case .sharing(let range): if let lowerBound = range.lowerBound { return lowerBound >= gate } return false case .declinedSharing: return false @unknown default: return false } } }}
The JS side simply calls it.
import { requireNativeModule } from "expo-modules-core";type AgeRangeModule = { checkLowerBound(gate: number): Promise<boolean>;};const AgeRange = requireNativeModule<AgeRangeModule>("AgeRange");export async function isAtLeast(gate: number): Promise<boolean> { // Not callable on non-iOS / old OS, so return false (fail safe) if (Platform.OS !== "ios") return false; try { return await AgeRange.checkLowerBound(gate); } catch { return false; }}
The mechanics of adding a native module in Expo are covered in a separate piece, setting up the Expo Dev Client and native modules, so if you don't have that foundation yet, start there. Here I focus on the design decision of confining age verification to the native side.
Placing It So Regeneration Doesn't Break It
When you build with Rork or Rork Max, screen code can get rewritten by AI regeneration. If you embed age-verification logic directly into a screen, it can vanish or mutate on each regeneration, leaving your operation unstable.
What I've settled on in practice is isolating age verification into a "non-regenerated layer." Concretely, I keep AgeRangeModule.swift and the JS-side isAtLeast() wrapper as fixed, hand-written, independent files, and have generated screens merely call that wrapper. Even if a screen is regenerated, as long as the call site contract (isAtLeast(16)) holds, the verification body stays intact.
This line between "the area that gets generated" and "the area you protect by hand" is a core idea in running Rork well, beyond just age verification. For the boundary design itself, the responsibility boundary between Rork Max and Expo is worth reading alongside this.
Fallbacks and the Limits of Client-Side Checks
Two things that are easy to miss, as closing notes.
First, OS-version fallback. Below iOS 26 the API doesn't exist, so branch with #available and make the policy explicit: on environments where it can't be called, either don't surface the feature or default to a safe state. Cutting off the experience entirely for users on older OSes in the name of age verification adds friction even for people who aren't in scope. Decide up front how far legal verification actually reaches, and keep the normal experience everywhere else.
Second, don't over-trust the client check. The Declared Age Range API is an on-device judgment, not a server-verifiable signed attestation. It suits client-side uses like ad tailoring or UI switching, but it's a poor fit for designs that hang a final payment or billing gate on age alone. Server-side logic should treat the age band from the client strictly as an advisory signal.
Your Next Step
Start by writing down one feature in your app you'd actually want to gate by age. Ad personalization? UGC display? A specific content category? Once that single point is settled, the age gate you should declare follows naturally.
Then hand-write one small function, isAtLeast(gate), and have regenerated screens call it. With that minimal scaffold in place, even as region-specific requirements like Texas's accumulate, the entry point for your checks stays consolidated in one spot.
I hope it gives a starting point to anyone else wrestling with age verification in their own solo development.
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.