Kicksharing Mobile App Development
Scooter is 50 meters away, app shows "charge 87%," QR-code scans — and nothing. Scooter control block froze, unlock command went out but no confirmation. User scans again, finally rides. Money charged twice. This isn't hypothetical — it's the typical situation with first versions of IoT commands without idempotency and transaction state.
Kicksharing Specifics Compared to Carsharing
Scooters are mass micromobility transport with cheap IoT blocks (Omni, Ninebot, Segway OEM lock). Each manufacturer's control protocol is different: Segway Lock Protocol, MQTT-based Omni API or proprietary TCP. Unlike a car, a scooter might be offline at unlock moment — and here Bluetooth unlock as fallback is important.
BLE unlock. Most modern IoT locks support BLE nearby: user approaches, app detects device via CBCentralManager (iOS) or BluetoothLeScanner (Android), sends encrypted unlock packet directly to lock without internet. Encryption key generated by server at rental start and transmitted to app beforehand — classic offline token scheme.
QR and NFC
QR scanning — most common scooter identification method. AVCaptureSession with AVMetadataObjectTypeQRCode on iOS, CameraX + BarcodeScanner from ML Kit on Android. Important point: don't build scanning overlay via SwiftUI ZStack over AVCapturePreviewLayer without explicit CALayer z-order — SwiftUI-view creates additional CALayer and may cover camera preview on some iPhone models.
NFC as QR alternative works via Core NFC NFCNDEFReaderSession (iOS) or NfcAdapter.enableForegroundDispatch (Android). On iOS read only in foreground, limiting scenarios, but for "apply phone to scooter" sufficient.
Geozones and Parking Rules
Kicksharing operators partner with cities: forbidden ride zones, mandatory parking zones, paid zones. These are GeoJSON polygons that the app downloads at startup and updates in background.
Point-in-polygon check: GMSGeometryContainsLocation (Google Maps) or MKPolygon.contains on iOS. For Flutter — poly_gon package or custom ray casting algorithm. When attempting to end rental in forbidden zone — block action with nearest allowed parking indicated.
Important: duplicate zone checks on server. Client might be old version or compromised.
Fleet Map and Clustering
With 1000+ scooters in a city clustering is mandatory. Use Supercluster (ported for iOS/Android/Flutter) — algorithm works on client, quickly rebuilds clusters on zoom change. At zoom > 15 switch to individual icons with battery indicator: green (>50%), yellow (20–50%), red (<20%).
Scooter position updates — WebSocket with server events or periodic refresh every 30 seconds with active map screen. Don't update entire fleet in background — waste of battery.
Tariff Mechanics
Kicksharing often uses composite rates: start + per-minute + peak hour coefficient. Keep tarification logic on server, app only displays current cost via WebSocket with update every 10 seconds during ride. Don't calculate cost on client — mismatch between what client showed and what server charged leads to disputes and chargebacks.
From Practice
Kicksharing startup, 400 scooters in one city. Problem: after iOS 17 update some users' BLE scanning stopped working — they got "Bluetooth unavailable" error though BT was on. Cause: change in CBCentralManager state restoration in iOS 17 — if app didn't implement CBCentralManagerDelegate.centralManager(_:willRestoreState:), CBManager threw .unknown instead of .poweredOn on state restoration. Fix took 2 hours after diagnosis.
Development Stages and Timeline
- Audit of scooter IoT blocks — protocol, BLE support, manufacturer API
- Architecture of commands with idempotency and offline BLE fallback
- Map development, QR/NFC, tarification, payment
- Integration with city geozones (GeoJSON from operator or city API)
- Publication
MVP (map → QR → rental → payment → completion): 10–14 weeks. Full platform with geozones, BLE, operation analytics: 4–6 months. Cost calculated individually.







