BOOK A CALL

All projects

Case study

Bolt

A weather app that opens on the last two hours of rain moving over you, and treats the fact that five forecast models disagree as the interesting part rather than something to average away.

Platform
iOS 17+
Stack
SwiftUI · MapKit · Core Image · zero dependencies
Status
TestFlight, build 14
Data
RainViewer · NASA GIBS · Open-Meteo · CAMS — all keyless

The idea

Most weather apps answer “what is the forecast”. The question that actually gets asked, standing in a doorway, is is that rain going to hit me — and the honest answer is a loop of the last two hours of radar, oriented on where you are standing.

So the app opens on the map, already animating, with no chrome to dismiss. The forecast is a drawer you pull up when you want it.

The decision worth defending

Show the disagreement

Five global models — ECMWF, GFS, ICON, GEM and UKMO — forecast the same hour differently, and every consumer weather app hides this by picking one or averaging them. Bolt draws all five on a shared domain and puts the spread under the reading.

Rainfall is where this stops being an aesthetic choice. Measured on a single afternoon hour, ECMWF forecast 0.03 inches and GFS forecast 0.40 — an order of magnitude apart. An average of those two describes no weather anyone will experience. The band between them does.

Everything is keyless

Radar tiles come from RainViewer, satellite cloud from NASA GIBS, forecasts and air quality from Open-Meteo and CAMS. None of it needs an account, a key, or a billing relationship — so the app has no server, no secrets to rotate, and nothing anyone can switch off.

Lightning is the exception, and it is labelled as such in the code: no free real-time strike feed exists, so strikes are inferred from WMO weather codes on a grid and sized by convective energy. It is a model, not an observation.

What the platform actually does

MapKit, measured rather than assumed

Almost every hard problem here was MapKit behaving in a way nothing reported. It will not request a tile more than one zoom level past maximumZ — silently, zero calls over sixteen seconds. It never calls loadTile when the overlay can build a URL, which quietly killed an entire retry-and-transform layer. It does not stretch a short tile image to fill a declared tile, which produced hard-edged rectangles of cloud that looked exactly like failed downloads.

And MKZoomScale bottoms out at 1/4096, which is why the basemap is vector rather than satellite: a scrim over imagery can only be an overlay renderer, and below that floor no arrangement of one can cover the screen. Coverage measured 1.000 at three degrees of span and 0.078 at a hundred and thirty.

The fix in each case came from instrumenting the thing and reading the numbers, not from reasoning about the documentation.

The shape of it

One screen with a map on it. Six overlays behind a dropdown — storm, temperature, feels-like, wind, air quality, UV — each animating across its own window of time, past through now into forecast. Pull the drawer up and the forecast is either a radar chart or a five-day meteogram, swiped between.

No account, no onboarding, no notifications, no tab bar.

Screens

The app opens here: two hours of radar and satellite cloud animating over your own location, with the temperature at nearby towns read straight off the forecast grid.
The app opens here: two hours of radar and satellite cloud animating over your own location, with the temperature at nearby towns read straight off the forecast grid.
Temperature as a field. There is no free tile service for modelled weather, so the map samples a 16×16 grid of points and scales it up — the blobs are Lanczos, not tiles.
Temperature as a field. There is no free tile service for modelled weather, so the map samples a 16×16 grid of points and scales it up — the blobs are Lanczos, not tiles.
Five models on one ring, sharing a domain so the outlines are comparable. The spread under the reading is the point of the view: 73–75 across five.
Five models on one ring, sharing a domain so the outlines are comparable. The spread under the reading is the point of the view: 73–75 across five.
The same forecast as a meteogram. Each measure is drawn as well as written, and comparing models lays a band across their disagreement with a tick on each.
The same forecast as a meteogram. Each measure is drawn as well as written, and comparing models lays a band across their disagreement with a tick on each.