Can I Ride?
Open the app and the whole screen turns green, amber or red for the hour you plan to leave. A verdict engine on the device, a thin proxy for the weather providers and a Pro tier on RevenueCat.
- Own product
- 2026
- iOS & Android
- Role: everything
- Status: live on Google Play



Every rider has the same morning ritual: open a weather app, scroll a ten-day grid, squint at a radar map and guess. The information is all there and the answer still is not. Can I Ride? was built by a rider tired of getting soaked on the way home, to replace that ritual with one screen and one word.
It is the fourth ZubiLabs product and the first with a server component, however small. The other three ship everything inside the binary; a weather app cannot, because the data comes from paid providers whose keys have to stay somewhere users cannot read them.
A forecast is not a decision. Rain at 40 % is a NO for a commute and a YES for a Sunday loop; 60 km/h gusts are fine on a tourer and unpleasant on a naked. The app had to turn numbers into a verdict a rider agrees with, and let each rider move the line.
A ride is also two legs. A dry morning with a storm at six in the evening is a NO, and the reason should say which leg is the problem. And the answer has to survive a phone with no signal in a garage: the last forecast must open instantly and say clearly that it is old.
A React Native app whose home screen is the answer. A huge YES or NO in the verdict colour, on a slow-breathing halo, with a headline and the reason underneath: "Rain expected in about 40 min", "Dry, but gusting at 65 km/h". Below it, a 24-hour strip with one coloured bar per slot; tapping a bar moves the departure. Chips at the top pick the place (GPS, home, work or a city search), the day and hour, and an optional return.
The rules run on the device, most to least severe: storm, snow or sleet; rain now; chance of rain above the tolerance; rain within three hours; then amber for ice, fog and wind; then green. A round trip combines both legs, the worse one wins and the reason names it. Free users get a 5-day forecast in 3-hour slots from OpenWeather; Pro unlocks 14 days, hourly precision and WeatherAPI, as a monthly, yearly or lifetime purchase. A daily "morning check" notification, a shareable verdict card, saved places, units and English and Spanish round it out.
Everything. Product, the verdict rules, brand and icon, the React Native app, the two API routes and their hardening, the provider mappers, the custom RevenueCat paywall, notifications and background task, Sentry, EAS builds, hosting and updates, unit tests, and the store listings in two languages.
Key decisions, and why:
- The verdict is a pure function. One module takes a forecast bundle, a time and the rider's thresholds and returns a tone and a reason. No React, no I/O, unit-tested rule by rule. The timeline, the round trip and the morning notification all call the same function, so they can never disagree.
- A proxy, not a backend. Two Expo Router API routes on EAS Hosting: forecast and geocode. They hold the provider keys, round coordinates to about a kilometre, cache for ten minutes and reject requests without the app token. No database, no accounts, nothing to migrate. Deploying the web export deploys the server.
- One provider shape, two providers. OpenWeather and WeatherAPI each get a pure mapper into the same ForecastBundle. The rest of the app never knows which one answered; the picker only reads the model's hour interval and maximum days. Swapping or adding a provider is one file and its tests.
- Pro is verified where it matters. RevenueCat's anonymous user id is the only identity. The app sends it with Pro requests and the forecast route asks RevenueCat's REST API before calling the paid provider, with the answer cached. Knowing the URL gets nobody a premium forecast.
- A morning notification the OS may never schedule. Background tasks on iOS and Android run when the system feels like it. The check runs from the background task and again every time the app comes to the foreground, inside a window from ninety minutes before the usual departure to thirty after, with a per-day flag so it fires once. Worst case, the verdict is there when the phone is picked up.
- A free quota anyone could burn. An open forecast route is a public weather API paid by someone else. The routes require a token embedded in the app, cache per rounded coordinate so neighbours share one call, validate every parameter, and gate the paid provider behind a server-side entitlement check. Without the secrets configured the checks are skipped and logged, so development stays simple.
- Offline that does not lie. The last forecast is persisted through TanStack Query so the app opens instantly with no signal, and the last GPS fix is kept for the same reason. Anything older than thirty minutes, or that failed to refresh, is flagged as a saved forecast with a cloud-off icon rather than passed off as live.
A finished 1.0: onboarding, the verdict with timeline and round trip, saved places and city search, thresholds and units, the morning check, the share card, offline cache, a custom paywall with three plans, crash reporting with masked session replay, 33 unit tests and listings in English and Spanish ready for both stores.
For ZubiLabs it is the case that shows the middle ground: a product with a server that is one deploy and no database, and a subscription that is enforced end to end rather than trusted on the client.
- Colour is the interface. The whole screen is the answer, readable with gloves on across a garage. Every other element earned its place by explaining or moving that answer.
- Keep the server as small as the secret. The only reason a server exists is that two API keys cannot ship in a binary. Two routes and a cache are enough; adding accounts or a database would have added cost and nothing the rider notices.
- Sell precision, not the answer. The free tier gives the verdict for right now and five days ahead; Pro sells two weeks and the exact hour. Hiding the answer itself would have made the free app pointless and the paid one resented.