DEV Community

Manuel Escrig
Manuel Escrig

Posted on Edited on Originally published at manuelescrig.hashnode.dev

Normalizing 77 years of MotoGP results into SQLite

Race Results app icon

Race Results is my MotoGP, Moto2 and Moto3 companion app for iOS. It has a live countdown, session times in your own time zone, standings, widgets and Live Activities. But the part I enjoyed building the most is the History tab: every season since 1949 🏍️

Don't trust the upstream API to stay around

The app reads from a public MotoGP results feed, but it doesn't depend on it for history. A small PHP backend normalizes the feed into SQLite, so finished seasons stay fast and keep working even if the provider changes something.

  • Lazy fill: when someone opens an old season, Grand Prix or session, the backend fetches it and stores it right then.
  • Full backfill: a resumable importer can load the whole premier-class archive, from 1949 until today, in one run.

Championships get renamed

The hardest part of the data model is that the classes changed names over the years. 500cc became MotoGP, 250cc became Moto2 and 125cc became Moto3. The archive tracks each class as one lineage, so records, champions and head-to-head comparisons work across eras. Records only count seasons where every finished race has structured results. For older sessions without data, the app links to the official PDF instead.

Live data on the Lock Screen

Live Activities brought their own bugs:

  • The Lock Screen leaderboard has to keep updating after the app restarts.
  • Lapped riders need to stay behind the lead lap in the live order.
  • The race Live Activity shows a lap counter instead of a clock, because races are measured in laps.

And time zones, of course 😅 For a while the Apple Watch showed session times shifted by the circuit's time difference.

Race Results App Store screenshots: schedule, race weekend, rider profile and news

Startup performance

Widget refreshes and other background work used to run during launch. Now they wait until the first screen is visible, and the Standings tab no longer rebuilds itself twice when you come back to the app.

Bonus: an MCP server

The same data also powers raceresults-mcp, a free hosted MCP server. It gives AI assistants tools like get_standings, get_calendar, get_next_race, get_race_results, get_rider, get_live_timing and get_news. If you already have clean data behind your app, exposing it to AI tools takes very little extra work. Give it a try!

Get Race Results on the App Store →

Website: raceresultsapp.com

Thanks for reading! Any MotoGP fans here? Tell me in the comments 🙂

More stories about the apps I build go out on my newsletter: manuelescrig.substack.com.

Manuel

Top comments (0)