◉RORK LABJP
●EXPO — EAS Observe now records native crashes (Oct 7). On SDK 57, update to 57.0.21 or later●SDK 58 — SDK 58 Beta has been out since Sep 15. The stable date is still unconfirmed●RN 0.88 — React Native 0.88.x is scheduled for Oct 12, 3 days left●Q&A — People are asking why expo-widgets render blank only in production builds●RORK — GPT-6.1 Sol was added on Sep 29, available on Pro and Max plans●NEW — Building an app for a client? Decide who owns the publishing account first●EXPO — EAS Observe now records native crashes (Oct 7). On SDK 57, update to 57.0.21 or later●SDK 58 — SDK 58 Beta has been out since Sep 15. The stable date is still unconfirmed●RN 0.88 — React Native 0.88.x is scheduled for Oct 12, 3 days left●Q&A — People are asking why expo-widgets render blank only in production builds●RORK — GPT-6.1 Sol was added on Sep 29, available on Pro and Max plans●NEW — Building an app for a client? Decide who owns the publishing account first
Articles/Dev Tools
⬡ Dev Tools/2026-07-08Advanced

Working Around Rork Max's 20-Geofence Wall with Dynamic Re-registration

In a native Swift app generated by Rork Max, geofences you registered quietly stop firing past a certain count — and it's almost always iOS's silent limit of 20 monitored regions per app. Here's a dynamic re-registration design that keeps only the nearest 20 live, plus a Swift implementation you can drop in.

Rork Max235Core Locationgeofencingregion monitoringSwift48iOS115

✦ Premium Article

I was adding a small location-aware feature to one of my indie apps: when you arrive at a place, show a quiet image chosen for that place. Nothing more. To power it, I registered about 30 spots across the country as geofences.

On my own iPhone, the handful of spots near me fired correctly. But the spots later in the list stayed silent even when I physically walked to them. No error in the logs. The CLLocationManager code Rork Max generated looked perfectly intact.

The cause wasn't the code — it was an iOS ceiling. Region monitoring can watch at most 20 regions per app. Anything past the 20th is ignored with no exception and no warning. This article covers a design built around that ceiling — keeping only the nearest 20 registered at any moment — and the implementation you add on top of what Rork Max gives you.

Why the 21st Region Goes Quiet

CLLocationManager region monitoring (startMonitoring(for:)) carries a limit Apple states plainly: a single app can monitor at most 20 regions. Try to register past that, and startMonitoring(for:) neither crashes nor returns false. The excess simply never joins monitoredRegions, and didEnterRegion never fires for it.

So the symptom looks like this:

Registration orderJoins monitoredRegions?didEnterRegion fires?
1st–20thYesYes
21st onwardNo (silently)No

Tell Rork Max "I want geofence notifications for 10 places," and it writes a straightforward for loop passing each one to startMonitoring(for:). At 10, nothing breaks. Only as your registration count grows — without anyone flagging the ceiling — does this surface as a low-reproducibility bug where "only the later ones don't respond."

For me, the part that ate the most time was that nothing errors. Core Location doesn't treat exceeding the limit as a failure, so even with print statements you only ever see "registered" logs. The first thing to check is manager.monitoredRegions.count right after registration. If it caps at 20 instead of climbing past it, the cause is confirmed as the limit, not a bug.

for region in regions {
    manager.startMonitoring(for: region)
}
// Always reconcile what you asked for against what's actually monitored
print("requested: \(regions.count) / actually monitoring: \(manager.monitoredRegions.count)")

The moment you see requested: 30 / actually monitoring: 20, the question changes from "why won't it fire" to "which 20 should I pick."

Don't Remove the Limit — Rotate Through It

You can't raise the 20-region ceiling from app configuration. Since there's no way to grow it, you change the design instead: hold as many logical geofences as you like in your own store, and hand CLLocationManager only the 20 nearest to the user at any moment. When the user moves, swap the lineup. This dynamic re-registration is the heart of it.

The question is when to swap. That's where startMonitoringSignificantLocationChanges() comes in. It's a separate mechanism from region monitoring, delivering location updates at a coarse grain — roughly every 500 meters, no more than once every few minutes. The coarseness keeps power draw low, and the OS wakes your app to deliver updates even after it has been terminated. That's far too coarse for the arrival test itself (radii around 100m), but it's exactly the right grain for deciding when to rotate the 20.

Laid out, the roles are:

RoleAPIGrain / traits
Actual arrival detectionregion monitoring~100m radius, max 20
Trigger to rotate the 20significant location change~500m, low power, relaunches after termination
Holding all geofencesyour own store (array or DB)unlimited count

It's a two-tier arrangement: reserve high-precision monitoring for a small set, and delegate the swapping to a coarse, power-frugal monitor. Even with hundreds of spots nationwide, only the nearest 20 are ever loaded into CLLocationManager.

✦

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'll understand why the 21st geofence is silently ignored, and be able to tell that the symptom is an iOS design limit rather than a bug in your code
✦You get a dynamic re-registration manager that always keeps the 20 targets nearest the user live, shaped to drop into Rork Max's generated code
✦You'll know exactly where to fill in the parts that only surface on a real device — significant location changes, didDetermineState, and Always authorization
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-04-05
Rork Max × WidgetKit & Live Activities in Practice — From Home Screen to Dynamic Island
A complete guide to implementing iOS Widgets, Lock Screen widgets, and Dynamic Island Live Activities with Rork Max. Covers WidgetKit fundamentals, Timeline update strategies, App Intents integration, and monetization.
⬡ Dev Tools2026-05-16
Migrating Firebase CocoaPods to SPM in Rork Max Apps
Firebase Apple SDK's CocoaPods distribution ends in October 2026. Here's a detailed migration log from moving 4 Rork Max iOS apps to Swift Package Manager — including dSYM failures, module errors, and the Dropbox conflict copy problem you'll definitely hit.
⬡ Dev Tools2026-05-01
Adding a Notification Service Extension to a Rork App — Rich Push, Encrypted Payloads, and Dynamic Rewriting
Wiring a Notification Service Extension into a Rork-built iOS app — image attachments, end-to-end encrypted payloads, and dynamic APNs payload rewriting — with working Swift code and the production pitfalls that bit me along the way.
📚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