Custom Cycling App Development with GPS & BLE Sensors
We build turnkey cycling apps from scratch — GPS tracking, sensor integration, and publication to App Store and Google Play. Over 5 years we've delivered 50+ projects for sports startups and cycling clubs. Our approach starts with niche analysis: why would a user choose your app over Strava or Komoot? The answer lies in a unique data model optimized for your specific scenario. We guarantee first-time acceptance in the App Store by strictly following Guideline 5.1 (privacy) and 4.2 (minimal functionality).
Problems We Solve
GPS Noise in Urban Environments
A raw track without filtering results in a jittery line and 15-20% inflated distance. Kalman filtering eliminates outliers and smooths the trajectory, improving accuracy by 20%.
BLE Sensor Connectivity
Users want real-time cadence, power, and heart rate. The BLE CSC (Cycling Speed and Cadence) profile is standard, but parsing requires handling unit measurement flags. We support up to 8 simultaneous BLE connections with data latency under 50 ms.
Offline Navigation with Turn-by-Turn Directions
In mountains, connectivity drops, but the map must work. We load regions in mbtiles format, and voice cues trigger 150-200 m before the turn via AVSpeechSynthesizer or TextToSpeech.
How We Solve GPS Noise?
The Kalman filter is our standard approach. A simplified smoothing algorithm for GPS:
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, we discard points with horizontalAccuracy > 50 m. Jumps exceeding 10 m/s (36 km/h) when expecting 25 km/h are also treated as outliers. This yields a track close to reality. The filter reduces positioning error by 20% compared to raw data. Apple Core Location Documentation
How to Integrate BLE Sensors Without ANT+ on iOS?
ANT+ is not available on iOS from the App Store (only via MFi accessories). On Android — the ant-android-sdk-stub library via the Ant+ Plugin Service. A realistic alternative is BLE sensors with dual ANT+/BLE broadcast (Garmin, Wahoo, most modern sensors). The protocol comparison is shown in the table below.
// 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 is computed from the wheel revolution delta and event time delta. Wheel circumference is a user setting or auto-calculated from tire size.
| Characteristic | BLE | ANT+ |
|---|---|---|
| Transmission latency | 5-15 ms | 1-5 ms |
| Power consumption | Medium | Low |
| Supported sensors | CSC, HR, Power, Speed, Cadence | Same + more variants |
| Availability on iOS | Yes (CoreBluetooth) | No (only MFi) |
| Availability on Android | Yes (Android BLE) | Yes (via plugin) |
For cross-platform projects, we use BLE as a universal solution: saving up to 40% of the budget due to a unified stack on both platforms.
Navigation by Route
Route building on cycle paths — OpenStreetMap via OSRM or GraphHopper with a cycling profile. Display — MapLibre GL (open source) or Mapbox SDK. Turn-by-turn voice navigation: AVSpeechSynthesizer on iOS, TextToSpeech on Android — trigger 150-200 m before the turn. Offline maps are critical for cycling tourism in mountains. mbtiles or PMTiles format, region download in advance. MapLibre supports offline out of the box.
Statistics and Integration
After the ride: distance, time, elevation gain (from CLLocationManager.altitude with barometric correction on iOS, similarly via SensorManager on Android), average/max speed, power, cadence. Export to .fit (Garmin/Strava format) or GPX. Integration with Strava via OAuth2 + Strava API v3: upload activity via POST /uploads with .fit or .gpx file. The endpoint supports multipart/form-data with fields data_type, name, activity_type.
Work Process
- Analytics. Research the niche, target audience, and competitors. Define the unique selling proposition.
- Design. Application architecture (Clean Architecture/REDUX), UI prototype via SwiftUI Preview or Jetpack Compose Preview.
- Implementation. Code in sprints, demo to client every 2 weeks.
- Testing. Unit tests, integration tests (XCTest/JUnit), testing on real devices with sensors.
- Deployment. Publish to App Store via TestFlight and Google Play via Internal Testing.
- Maintenance. Crash monitoring (Firebase Crashlytics), updates for new iOS/Android versions, 3-month support.
Comparison of Approaches: iOS vs Android for GPS Tracking
| Criterion | iOS (Swift) | Android (Kotlin) |
|---|---|---|
| GPS provider | CLLocationManager | Fused Location Provider |
| Background activity | Always Authorization + allowsBackgroundLocationUpdates | Foreground Service + ACCESS_BACKGROUND_LOCATION |
| BLE stack | CoreBluetooth (BLE), ANT+ unavailable | Android BLE API, ANT+ via plugin |
| Navigation | MapKit or MapLibre | MapLibre or Maps SDK |
| Offline | MapKit with cache or MapLibre | MapLibre with cache |
| Development time | 8–12 weeks | 8–12 weeks |
BLE is better than ANT+ on iOS for availability, but ANT+ gives lower latency on Android. For cross-platform projects we use Flutter — it allows reusing 80% of the code, saving up to 40% of the budget.
Why Choose Us
- Experience: Over 5 years developing sports apps, 50+ completed projects.
- Certifications: Our engineers hold Apple and Google certifications (Apple Certified iOS Developer, Google Associate Android Developer).
- Guarantee: First-time acceptance in stores — we follow App Store Review Guidelines (4.2, 5.1) and Google Play Policy.
- Transparency: Every 2 weeks a demo and progress report.
What's Included
- Documentation: Application architecture, API description, build and deployment instructions.
- Source code: Private GitHub repository with configured CI/CD (GitHub Actions/Xcode Cloud/Android Build).
- Access: TestFlight for iOS, Firebase App Distribution for Android, developer consoles.
- Training: Documentation and a call for the support team.
- Warranty: Bug fixes within 3 months after release.
Checklist for Development Preparation
- Core functionality defined (GPS, sensors, offline maps)
- Platform selected: iOS, Android, or both (cross-platform)
- Unique features differentiating from competitors outlined
- Icons and brand materials prepared (App Store/Google Play)
- Budget and timeline agreed upon
Contact us to discuss your project — we'll estimate the task in 2-3 business days and propose the optimal solution. Order your cycling app development and receive a test version for review within two days.







