Developing a Mobile App for Cyclists
Cycling apps—competitive market with Strava, Komoot, Wahoo. Survive either niche products (trekking for specific region, club features, specific hardware integration) or apps with unique data model. Development starts with understanding why user needs another app and what makes it better.
GPS Tracking: Data Quality Over Frequency
CLLocationManager on iOS with desiredAccuracy = kCLLocationAccuracyBestForNavigation and distanceFilter = 5 (update every 5 meters)—standard for activity tracking. Android—Fused Location Provider with PRIORITY_HIGH_ACCURACY and 1-2 second interval.
GPS problem on bike—not accuracy in open field but tunnels, overpasses, dense urban. Points jump 50-100 meters. Raw track without filtering—jerky line on map and inflated mileage.
Kalman filtering—standard approach. Simple GPS smoothing algorithm:
class GPSKalmanFilter {
private var latitude: Double = 0
private var longitude: Double = 0
private var variance: Double = -1
private let minAccuracy: Double = 1.0
mutating func process(lat: Double, lon: Double, accuracy: Double, timestamp: TimeInterval) -> CLLocationCoordinate2D {
let accuracy = max(accuracy, minAccuracy)
if variance < 0 {
latitude = lat; longitude = lon
variance = accuracy * accuracy
} else {
let timeStep = timestamp - lastTimestamp
if timeStep > 0 {
variance += timeStep * 3.0 // Q: process noise
}
let K = variance / (variance + accuracy * accuracy)
latitude += K * (lat - latitude)
longitude += K * (lon - longitude)
variance = (1 - K) * variance
}
lastTimestamp = timestamp
return CLLocationCoordinate2D(latitude: latitude, longitude: longitude)
}
}
Additionally: discard points with horizontalAccuracy > 50 meters completely. Jump over 10 m/s (36 km/h) at expected 25 km/h—also outlier.
Bluetooth Sensors via ANT+/BLE Protocol
Cyclists use external sensors: cadence sensor (pedal RPM), power meter (wattage), heart rate monitor, speed sensor. Protocols—Bluetooth LE with GATT Cycling Profile (CP, CSC, HR) and proprietary ANT+.
ANT+ on iOS closed from App Store (only MFi accessories). Android—ant-android-sdk-stub library via Ant+ Plugin Service. Realistic alternative—dual ANT+/BLE translation sensors (Garmin, Wahoo, most modern).
// Android: subscribe to Cycling Speed and Cadence (CSC) GATT
val CSC_SERVICE = UUID.fromString("00001816-0000-1000-8000-00805f9b34fb")
val CSC_MEASUREMENT = UUID.fromString("00002a5b-0000-1000-8000-00805f9b34fb")
fun parseCscMeasurement(value: ByteArray): CscData {
val flags = value[0].toInt()
var offset = 1
var wheelRevolutions = 0L
var wheelEventTime = 0
if (flags and 0x01 != 0) { // Wheel Revolution Data present
wheelRevolutions = value.getLong32(offset)
wheelEventTime = value.getUInt16(offset + 4)
offset += 6
}
var crankRevolutions = 0
var crankEventTime = 0
if (flags and 0x02 != 0) { // Crank Revolution Data present
crankRevolutions = value.getUInt16(offset)
crankEventTime = value.getUInt16(offset + 2)
}
return CscData(wheelRevolutions, wheelEventTime, crankRevolutions, crankEventTime)
}
Speed from CSC calculated by wheel revolution delta and event time delta. Wheel circumference—user config or auto-calculate by tire size.
Route Navigation
Build route via bikeable roads—OpenStreetMap via OSRM or GraphHopper with bike profile. Display—MapLibre GL (open) or Mapbox SDK. Step-by-step voice navigation: AVSpeechSynthesizer on iOS, TextToSpeech on Android—trigger 150-200 meters before turn.
Offline maps critical for bike touring in mountains. Mbtiles or PMTiles format, download region beforehand. MapLibre supports offline out-of-box.
Stats and Integration
After ride: distance, time, elevation gain (from CLLocationManager.altitude with barometric correction on iOS, analogous via SensorManager on Android), average/max speed, power, cadence. Export to .fit (Garmin/Strava format) or GPX.
Strava integration via OAuth2 + Strava API v3: upload activity via POST /uploads with .fit or .gpx file. Endpoint supports multipart/form-data with data_type, name, activity_type fields.
Developing cycling app with GPS tracking, BLE sensors, map, Strava export: 8-12 weeks per platform. Cost individually quoted.







