IoT Device Geofencing in Mobile App
Geofence triggered — tracker entered zone. But notification came 4 minutes later. By then the scooter moved 600 meters past the warehouse perimeter. Problem is where the geofence is checked: on server every N seconds or in real-time on every incoming packet.
Server-side vs client-side geolocation
For IoT devices (trackers, sensors, vehicles), geofences always checked on server. IoT device doesn't run your app — it just sends GPS packets. Mobile app gets computed event via push or WebSocket.
This contrasts with scenario where geofence checked by user's phone via CLLocationManager.startMonitoring(for region:) (iOS) or GeofencingClient.addGeofences() (Android). But for IoT fleet — server-side only.
How server geofence works
PostgreSQL + PostGIS — standard stack:
SELECT zone_id, zone_name
FROM geofences
WHERE ST_Contains(geometry, ST_SetSRID(ST_MakePoint($lon, $lat), 4326));
GIST index on geometry accelerates ST_Contains to microseconds. At 500 incoming packets per second from entire fleet — load acceptable on single PostgreSQL instance.
State transition enter/exit determined via state machine in Redis: compare each event with previous device state. If was outside → became inside → emit geofence_entered event.
Creating and editing geofences in app
User draws geofence on map — circle or arbitrary polygon.
Circular zone. Simplest: GMSCircle (Android) / MKCircle (iOS) with center and radius. User taps center, drags handle to set radius. Stored as {lat, lng, radius_meters}.
Polygon zone. User taps points on map, forming closed outline. GMSPolygon / MKPolygon with live preview as points added. Last point connects to first automatically on "Close figure" press.
Editing: drag vertices (draggable Marker on corners), add intermediate points via midpoint handles. Standard pattern for polygon editor on maps.
Validation: self-intersecting polygon — ST_IsValid(geometry) on server before save. Client gets error and highlights problem section.
Notifications and reactions
Geofence triggers, server sends push via FCM (Android) / APNs (iOS) with priority: high. For time-critical alerts (child left safe zone, truck left perimeter) use APNs content-available: 0 (visible push) — arrives even in Android Doze mode.
Notification contains: device name, zone name, event type (entry/exit), time. On iOS — UNNotificationCategory with "Open map" action for quick switch. Deep link in userInfo payload.
In app — event feed of geofences with filtering by device/zone/event type. Pagination via cursor-based pagination (not offset — events table grows fast).
Sensitivity tuning
Tracker sends point every 10–30 seconds. At this frequency device can "skip through" thin geofence without single point inside. Solutions:
- Increase recording frequency near zone — some trackers support "alert zone" and auto-increase frequency
- Check
[prev_point, cur_point]segment intersection with zone boundary viaST_Intersects(ST_MakeLine(...), geometry)— catches transient crossings - Buffering: expand zone by N meters (
ST_Buffer) for early warning
Timeline
Implementing geofences (polygon creation/editing + boundary crossing notifications) with ready server part: 3–5 working days. If server logic needs development with PostGIS + Redis state machine — 1–2 weeks. Cost calculated individually.







