Build Sheet · July 2026

How SunnySky is built

A dark-mode, social-first weather app that reads sixindependent forecasts and shows you where they disagree. One TypeScript codebase ships it to iPhone, Android and the web. Here's what I used, why I picked it, and what it costs me to run.

TypeScript
26,492
Forecast sources
6
Data providers
20
Edge functions
12
Cost today
$8/mo

01The shape of it

It's one repo with four packages inside. Two of them are the actual apps — the phone app and the website. The other two are the stuff both apps have to agree on: all the weather logic, and the design tokens.

That split is basically the whole architecture, and it's the decision I'd defend hardest. The maths that draws the consensus line on the chart is literally the same function that fills in the iPhone widget, because they both import it from the same file. There's no second copy to drift out of sync. When I fixed a rounding bug in it once, the app, the website and the widget all got the fix at the same time without me touching three files.

Everything below hangs off that idea: keep the thinking in one place, keep the platforms dumb, and don't pay for anything the government already gives away.

02What I'm actually using

The phone app

I'm a solo dev. If I had to maintain a Swift app and a Kotlin app and a web app separately, I'd ship nothing. So the whole client is one codebase.

ExpoSDK 57
The thing that makes one codebase into three apps. It also handles the annoying parts I don't want to own — push notifications, location permissions, image caching, over-the-air updates. Expo used to mean “you'll hit a wall eventually” and that's just not true any more; I ship a real SwiftUI widget from it.
Real native views underneath, not a website in a wrapper. It matters here because the app is meant to feel like an instrument — scrubbing the chart has to track your thumb exactly, and a webview would feel like mush.
Routing based on the file layout, like Next.js but for the phone. The bit I actually love is Stack.Protected— the whole onboarding → login → app gate is four lines of “this screen only exists if you're signed in”, instead of redirect logic smeared across every screen.
I draw the forecast chart by hand with this. I looked at chart libraries first and none of them do what I needed: a median line whose colour changes along its length with the temperature, the other sources ghosted behind it, and a scrubber that snaps to 30-minute steps. Bending a library into that shape is more work than just drawing it.
Lets me add a genuine SwiftUI widget extension to an Expo project without ejecting. This is the one that surprised me — home screen widgets used to be a reason to go bare React Native, and now they're a config file.

The website

sunnyskyweather.com is the marketing site, and it also serves the entire app as a web build at /app — so you can try the real thing without installing anything.

Mostly here for one feature: when you share a location from the app, Next generates a live weather image for that exact spot on the fly. That share card is the only thing people who don't have the app ever see, so it's worth a framework.
Pulls in the same design tokens the phone app uses, so the site and the app are genuinely one visual system instead of two things that sort of look alike. This page is styled from those tokens too.
Hosting. One command to deploy, and it just works. Heads up if you're copying this: their free Hobby plan is licensed for non-commercial use only. This is a commercial product — it sells a subscription — so the scale estimate below includes their $20/mo plan.

The backend

This is the part I'd have expected to be hardest and it turned out to be the easiest. It's all one product.

SupabasePostgres · 18 tables
Real Postgres, not a document store, and that's deliberate. The social side is a proper relational mess — following someone is a requestthat gets accepted or declined, answers point back at questions, blocking someone has to filter them out of two different feeds. That's a join problem, and Firebase would have had me writing it by hand.
The rule about who can see what lives in the database, not in my app code. So if I write a buggy screen, it still can't show you someone else's private report — the query just doesn't return the row. This is the single best thing about building on Postgres and it's why I'd struggle to go back.
Edge FunctionsDeno · 12 of them
Little Deno functions that run on Supabase. Every paid weather API key lives in one of these instead of in the app, because a key you ship inside an app is a public key — anyone can pull it out of the bundle. They also cache the weather per area, so a hundred people in one city cost me one API call instead of a hundred.
pg_cron + pg_net5 scheduled jobs
The database schedules its own work. Severe weather sweep every 5 minutes, morning brief every hour, sky events every 3 hours, forecast accuracy logging every night. There is no serversitting somewhere running a queue — it's a cron entry in Postgres that pokes a function.
Supabase Authemail OTP
Passwordless — you get a code by email. No passwords to store, leak, or reset, and one fewer screen to build.

Where the weather comes from

This is the actual product. Most weather apps resell one forecast. I pull six, plus a pile of government feeds, and show you where they disagree.

Six forecaststhe consensus
Open-Meteo, MET Norway, the US National Weather Service, Apple WeatherKit, Pirate Weather and OpenWeather. When they disagree the app says so out loud instead of quietly averaging them into a number nobody should trust.
Ten modelsrain vote
Underneath the forecasts are the actual weather models — ECMWF, GFS, ICON, HRRR, UK Met Office, Canada's GEM, Météo-France, Japan's JMA, China's CMA and ECMWF's AIFS, an AI model. I ask all ten whether it rains and count the votes. It costs nothing extra— they arrive on a request I'm already making for the chart.
Government feedsfree, no key
This is the unfair advantage. Storm Prediction Center outlooks, hurricane tracks, NOAA tides, USGS river gauges, airport observations, official climate normals, space weather for the aurora, NASA satellite imagery, radar straight from the services that run the networks — NOAA/NWS MRMS across the US and its territories, Environment and Climate Change Canada across Canada, which is why radar stops at the edge of North America instead of quietly going blank — and lightning from the Geostationary Lightning Mapper aboard GOES-19 — total lightning, Americas only, decoded out of raw satellite granules every five minutes. All public, all keyless, all $0.

Shipping and keeping it alive

Builds the iOS app in the cloud and pushes it to TestFlight in one command. I don't own a build machine and never want to.
Sentry7.11
Tells me when the app crashes on someone else's phone, with the line number, app version and technical device context. The top-level React fault screen reports caught render failures too. Native only — the web build deliberately reports nothing.
First-party product analyticsPostgres · owner-only
A random installation ID, foreground sessions, screen views and a small reviewed set of product actions go through narrow Supabase functions into private tables. There is no generic bag of event properties, so coordinates, account profile fields, user-written content and raw URLs cannot quietly find their way in. Durable facts such as account, report and ask creation are recorded by database triggers in the same transaction as the action. This measures what happens after launch; it does not pretend a session is a download. Apple and Google remain the sources for store downloads and acquisition data. This website keeps one daily tally per page — the page, a campaign tag, the referring site — and nothing else: no cookie, no IP address, no identifier, and a shared card's location collapsed away before it is counted.
Runs typecheck and builds across all four packages and skips anything that hasn't changed. Keeps a monorepo from feeling slow.
TypeScript5.8 · strict
Strict mode everywhere. The shared packages export raw source, so there's no build step between me changing a file and both apps seeing it — and the compiler catches the mismatch immediately if I break one of them.

03What it costs me

Two columns — what I'm actually paying right now, and what it turns into at roughly 2,000 installs. These are operating costs; what users pay is under the table.

ServiceWhat I needNowAt 2k installs
Apple Developer$99/yr, non-negotiable$8.25$8.25
Open-MeteoAPI Standard — commercial licence$29.00$29.00
SupabasePro — backups, and it stops auto-pausing$0$25.00
VercelPro — commercial hosting$0$20.00
Pirate WeatherPaid once I outgrow the free calls$0$5.00
Apple WeatherKit500k calls included with the $99$0$0
OpenWeatherFree tier is plenty$0$0
NOAA / NASA / USGSPublic data, no key, no bill$0$0
SentryFree — 5k errors/mo$0$0
EAS BuildFree — 15 iOS builds/mo$0$0
What I payper month$37.25$87.25

What users pay:

Free — warnings, radar, every source$0
Pro, monthly$4.99
Pro, yearly$29.99
The founding hundred$0 for life
Subscribers to break even at 2k25–50

The break-even line is small on purpose: 25 subscribers cover the 2,000-install bill if they all pay monthly, 50if they all pay yearly — and that is after Apple's full 30%. Severe weather alerts are not in Pro and never will be, so nothing about staying safe depends on reaching it. The budget stays manageable because the expensive-sounding parts — six forecasts reconciled, ten models polled, months of accuracy history — mostly run on free government data and on maths that happens on your phone.

There used to be three Google rows here — Google Weather as a source, Google Pollen outside Europe, and Google Routes for drives off iPhone. I took all three out in September 2026: Google Maps Platform's terms name a weather app as a use its Weather API isn't for, and cap how long its weather may be kept, and a weather app built on remembering forecasts can't live inside that. Pollen now comes from Copernicus CAMS, which covers Europe, and a route planned anywhere but an iPhone is a straight line between the two ends, stamped as one.

04Stuff I learned the annoying way

The licences come before the price

Open-Meteo's free API is non-commercial. Vercel's Hobby plan is non-commercial too. Charging users was never the test: SunnySky was a commercial product before it sold anything, and an app with a subscription in it is one by anyone's reading. The Open-Meteo licence was bought the day billing was switched on, not after the first sale.

At scale the licensed plans are $49/mo between them. Pro exists to pay for things like that — it is not a toll on the warnings.

The provider tier shapes the product

SunnySky stays on Open-Meteo's API Standard plan. Historical, climate, ensemble, and satellite-radiation endpoints require the Professional plan, so the app does not call them.

The multi-model rain vote and official NOAA climate normals answer the useful questions on the Standard plan, so Pro's price doesn't have to subsidise the Professional one.

Where the money isn't going

No servers. No Kubernetes, no Redis, no message queue, no CDN bill, no third-party product analytics vendor, no Mac in a cupboard running builds. The database schedules its own jobs and Apple handles app signing and distribution.

05Five things I deliberately didn't use

Every one of these is a thing you'd normally reach for, and skipping them was the right call each time.

No charting library

I hand-draw the forecast chart in SVG. I needed a median line coloured by temperature along its length, every source ghosted behind it, and a scrubber snapping to 30-minute steps — and once you've fought a chart library for a day you realise drawing it yourself is the shorter path.

No map library

The radar map is about 400 lines of hand-rolled tile maths. It draws the basemap, the radar frames and the warning polygons, animates them, and behaves the same on phone and browser. Mapbox and friends bill per map load, which is exactly the cost curve I built the rest of this app to avoid.

No state management library

Just React context and some hooks. The app's state is honestly simple — a location, a forecast, a session. Redux would be ceremony for the sake of it.

No global radar mosaic

The free worldwide mosaics are licensed for personal and educational use, with no commercial tier on sale at any price. The vendors who will sell you one quote four figures a month at this app's size, and what they sell is a satellite-blended field stitched across the places with no radar in them — which I could not put a RADAR stamp on and still look you in the eye. So radar is the real thing over North America and nothing at all everywhere else.

No component kit

Design tokens instead. The app has a very specific look — instrument panel, monospace numbers, true-grey surfaces with no blue in them — and starting from someone else's components means fighting their defaults the whole way.