-
-
Offline: the test catalogue, with "Works offline" and "Test 8 · 100%" chips.
-
Stats: a "Pass probability 87%" card.
-
The problem: "30 questions. 30 minutes. Only 3 mistakes."
-
Exam mode: the timer and the selected answer lift out of the phone.
-
Road signs: the "Ceda el paso" tile and the "Correcta" card.
-
Vroom AI: the user's question and the AI's answer.
-
End card: "Now on the App Store", vroomdgt.com and "Paywall powered by RevenueCat · #Shipaton".
-
By the numbers: 2,448 questions · 440 signs · 150 tests.
-
Cover: logo, tagline and three phones fanned out on Vroom Blue.
-
Without iphone frame
-
Without iphone frame
-
Without iphone frame
-
Without iphone frame
-
Without iphone frame
-
main hub
Inspiration
In Spain, hundreds of thousands of people a year sit the DGT theory exam for a category B licence. They study by grinding through question banks, and then they walk into the test centre with no idea whether they are ready. The apps they use answer the wrong question: they tell you how many tests you have done, not whether you are going to pass.
That gap is expensive. Failing means paying the exam fee again, waiting weeks for a new slot, and in most cases paying the driving school for the extra time. Everyone we asked described the same feeling: I think I'm ready, but I'm going because my instructor told me to.
Vroom DGT was built to replace that feeling with a number.
What it does
Vroom DGT is an iOS app that prepares you for the Spanish theory exam and tells you when you are ready for it.
Inside are 810 category B questions, each with an explanation of why the answer is right, the 440 official sign plates, the full syllabus, and five ways to practise: classic tests, timed mock exams under real exam rules, a one-life survival mode, sign drills in both directions (plate to name and name to plate), and a review deck holding everything you have ever got wrong until you get it right.
All of it works offline, with no account and no sign-up. And on the stats screen there is the thing nothing else gives you: your probability of passing, computed from your own mock exams, next to a countdown to your exam date.
How we built it
The app is Expo (React Native) + TypeScript, with expo-router and NativeWind over a design system ported screen by screen rather than invented.
The architecture is offline-first, without exceptions. Every write goes to local SQLite first and lands in a sync queue; a worker drains that queue into Supabase whenever there is a network. The app never blocks on connectivity, which is what lets someone sit a full mock exam on the metro. Session IDs are UUIDs generated on the client, because a server-generated ID would mean a session cannot exist before it has network, and that breaks the whole premise.
Auth is an anonymous Supabase session that can later be linked to Sign in with Apple. Payments run through RevenueCat, so the paywall can be redesigned and A/B tested from a dashboard without shipping a build. Product analytics run on PostHog in the EU, wrapped behind one module with autocapture off, so no user-written text ever leaves the device.
Challenges we ran into
React Native cannot require() a dynamic path. With 440 sign plates plus question images, the asset map has to be a file with one static require per image, so it became a generated artifact of the content pipeline, next to a validator that fails the build on a broken image reference or a duplicate question ID.
One identity call away from deleting everyone's progress. Linking an anonymous account to Apple uses linkIdentity(). Its neighbour in the SDK, signInWithIdToken(), has an almost identical signature and does the opposite: it signs you in as the Apple identity and abandons the anonymous user, with all the local progress still inside it. We found it in a spike, not in production, which is the only reason this is a paragraph and not a post-mortem.
Access control has more doors than you think. One function arbitrates every entitlement check, and it denies when in doubt. Removing the close button from the paywall only covered the front door: there turned out to be seven ways into the app, including deep links, restoring a purchase that does not exist, and losing access while the app is already open.
Killing features we had already built. Spaced repetition with five Leitner boxes went in, worked exactly as designed, and shipped nothing useful: a correct answer promoted the card into the future, so the deck never emptied and grinding it never moved the counter. It was replaced by a two-state model, pending or resolved. XP and ranks went the same way, for the same reason.
Accomplishments that we're proud of
It shipped. Vroom DGT is live on the App Store, built and maintained by one developer, and has gone through several releases since launch.
The pass probability actually works. It is a real statistic with a calibration gate, an honest footnote, and a property we can defend: it does not move unless you do something.
Genuinely offline. Not "degrades gracefully". You can install it, put the phone in airplane mode, study for a week, and everything syncs when you land.
We deleted more than we kept. Rapid Fire, XP, ranks, Leitner, a 23-screen onboarding. Every one of them was working code, and the app is better for each removal.
Privacy that survives reading the code. No ad SDKs, no IDFA, no tracking, EU-hosted analytics, and not a single event carrying text the user typed.
What we learned
The thing we underestimated was instrumentation. Our first real cohort analysis said that 62.6% of users finished onboarding, 44.8% ever did a single test, and that of the people who dropped out during onboarding, not one had ever seen a question. They did not try the product and dislike it. They left before reaching it. That reframed the whole roadmap: the problem was not the tests, it was everything standing in front of them. We had to move profile creation from the last onboarding screen to the first just to see who was leaving and where.
The other lesson shows up in the formula, the paywall and the review deck alike: honest and illegible is still a failure. The pass probability used to be a Wilson confidence interval, statistically impeccable and completely unreadable. It is now one number with a traffic-light colour, a calibration gate, and a line underneath saying this is an estimate and not a promise. Same honesty, delivered in a form someone will actually use.
What's next for Vroom DGT
Practice the topic you are worst at. Today the app tells you where you are weak and then cannot do anything about it: the classic tests run on a frozen, shuffled catalogue, so there is no way to ask for 30 questions on intersections. The sampler already exists in the content layer, unused. This is the piece that turns "your plan" from a description into something the app actually executes. Rework the Hub around what the data says, once sign practice stops being a measurement blind spot. Right now the most prominent row on the main screen belongs to the one mode we know the least about. Paywall and pricing experiments, including an annual plan, run as A/B tests through RevenueCat with no build required. More of the question bank surfaced, with difficulty tagging done as reviewed content work rather than a script guessing. Beyond category B: motorcycle and other permits share the engine, the sync layer and the statistics. Only the content changes.
Built With
- applaunchpad
- chatgpt
- claude
- react
- react-native
- revenuecat
- supabase
Log in or sign up for Devpost to join the conversation.