Inspiration
About two years ago, I downloaded Flighty. Plenty of apps can track flights, but Flighty felt different: it was unmistakably native to iOS, thoughtfully animated, and designed around the traveler rather than the underlying data. It became one of my favorite apps.
Last month, while traveling through Japan, I spent a lot of time riding the Shinkansen. The rail system was extraordinary, but the information I needed was often spread across timetables, station displays, and different services. I looked for a train companion with the same clarity and craftsmanship that Flighty brings to flying. There were useful rail apps, but nothing matched the experience I had in mind.
That became the idea behind Trainy: what if traveling by train felt as understandable and beautifully organized as tracking a flight? I wanted one native iOS experience that could guide a rider from platform to destination while remaining honest about where its information came from and how current it was.
What it does
Trainy is a native iOS rail companion that brings trip progress, platforms, station boards, route maps, stops, service alerts, and connection context into one clear experience.
The current version has two rider-active provider experiences:
- Japan Shinkansen: Riders can search for and track services across Japan's major high-speed rail lines. Trainy works without credentials using a curated starter catalog and can also use ODPT and official JR timetable data when configured.
- Netherlands NS: Riders can search NS stations, view current departures, and see active disruptions through Trainy's secure production proxy.
Trainy describes information as real-time only when the source supports that claim. Starter, scheduled, fresh, stale, and realtime data are labelled separately, so a route marker or timetable is never presented as a live train position.
For tracked journeys, Trainy can show route progress, the next stop, estimated and scheduled times, platforms, station timelines, car-positioning guidance, alerts, connection risk, and source freshness. Riders can also save trips, pin services, configure notifications, and adjust display preferences.
The app includes the foundation for a travel-history experience with statistics such as delays, travel time, distance, operators, and regions. Some of those longer-term metrics still need to be completed as Trainy moves beyond its current beta.
How we built it
Trainy was a solo project, so “we” largely means me working alongside Codex and GPT-5.6.
The native app is written in Swift and SwiftUI, with MapKit for journey visualization. It uses a package-first architecture: reusable models, provider adapters, persistence, views, and design-system components live in TrainyCore, while a thin Xcode target owns the iOS lifecycle, resources, privacy manifest, and packaging.
Rail providers expose very different schemas and capabilities, so I created a provider registry and normalized rail models inside the app. This lets the interface reason consistently about schedules, station boards, alerts, availability, authentication requirements, and source provenance without pretending every provider offers identical data.
The Netherlands integration required a server-side boundary because the NS subscription key cannot be safely shipped within an iPhone app. I built a narrowly scoped Cloudflare Worker that exposes only the fixed station search, departure, disruption, and health operations. It validates inputs, normalizes upstream responses, applies bounded caching and rate limits, and keeps the provider credential entirely server-side.
I initially worked with GPT-5.5 and switched to open-source models through Ollama when I reached usage limits. Those models were useful, but GPT-5.6 changed the pace of the project. It could take a substantial goal, inspect the existing repository, form a plan, implement across multiple layers, run the relevant checks, interpret failures, and continue until the completion gates were satisfied.
My role resembled a product owner and engineering lead. I defined the intended experience, constraints, security boundaries, and definition of done. Codex and GPT-5.6 helped trace the codebase, implement features, create tests, diagnose failures, respond to review feedback, and document the evidence. I still reviewed the work and kept credentials, deployments, signing decisions, and submission actions under human control.
Challenges we ran into
The hardest challenge was that rail providers are not standardized. They use different authentication systems, identifiers, schemas, rate limits, terms, and definitions of “live.” Some provide detailed real-time updates, while others expose only schedules or station boards. Provider access also does not necessarily mean the data is ready to use inside a rider-facing product.
Instead of forcing every source directly into the interface, I built normalization boundaries around the data. That made it easier to integrate new providers while keeping their actual capabilities visible. I had a much longer provider roadmap—and certainly would not have objected to those promised 750 tokens per second—but I chose to finish two trustworthy paths rather than claim ten incomplete integrations.
Protecting credentials was another major challenge. The NS key had to remain outside the app, logs, test artifacts, and repository. That led to the Cloudflare Worker, strict env-file parsers, secret-boundary regression tests, and an archive scanner that checks final build outputs rather than trusting source inspection alone.
The final challenge was truthfulness. A polished interface can accidentally make estimated, scheduled, or fallback information appear live. Trainy therefore carries source, confidence, and freshness metadata through the provider layer and into the UI. Loading, stale, offline, rate-limited, and unavailable states are treated as real product experiences rather than edge cases.
Accomplishments that we're proud of
I am proud that Trainy became a working native product rather than remaining a design concept or a “coming soon” page.
The current app includes accessible onboarding, Shinkansen trip discovery and tracking, a production-backed Netherlands station experience, truthful provider status, source-aware failure recovery, Light and Dark Mode, and AX2XL accessibility behavior.
I am especially proud of completing the full NS path: native app, provider adapter, secure Worker, upstream quota protection, credential-safe normalization, deterministic fixtures, simulator automation, and production verification. The provider is only described as rider-active because the entire path was tested.
The most recent audited release baseline passed:
- 66 of 66 Xcode tests
- 35 of 35 Cloudflare Worker contract tests
- 27 of 27 design-system guard fixtures
- 44 Release archive checks with zero failures
The Release archive was also inspected for privacy declarations, debug artifacts, local paths, provider provenance, Crashlytics behavior, and known credential fingerprints. Trainy is not App Store-distributed yet, but its contents have been audited as a serious release candidate.
For a solo project, completing the product, backend boundary, accessibility work, deterministic automation, and release audit within the hackathon window feels like a meaningful accomplishment.
What we learned
The biggest lesson was that agentic AI works best when it receives more than a broad request. GPT-5.6 became dramatically more effective when I provided a concrete goal, repository context, constraints, completion gates, and permission boundaries. The strongest results came from a cycle of inspecting, planning, implementing, testing, and auditing—not from a single prompt.
I also learned that an AI coding agent can function like an engineering team, but it still benefits from clear human ownership. I made the product and risk decisions; Codex helped execute and verify them at a scale I could not have reached alone in the same amount of time.
This project also changed how I think about data provenance. Source labels, freshness, fallback behavior, and honest unavailable states are not backend details. They are part of the rider experience. A beautiful interface is only useful when people can trust what it tells them.
Finally, I learned to value complete vertical slices over broad but shallow integrations. Two providers that work from source to simulator are more valuable than a long list of logos backed only by credentials or unfinished adapters.
What's next for Trainy
The next phase is polish, polish, polish.
My goal is to finish distribution signing, begin TestFlight testing, and work toward a public iOS release by the end of the summer. Before that happens, I want to refine the onboarding and interaction details, complete the travel-history metrics, automate more release checks, and validate Trainy with real riders.
I also plan to expand the provider network carefully. Taiwan TDX, Hong Kong MTR, shared GTFS/GTFS-Realtime support, and additional European and North American rail systems are on the roadmap. Each provider will be added only after its complete data, attribution, credential, fallback, and rider-facing path has been verified.
Longer term, I expect Trainy to offer both free and paid experiences. The source is publicly reviewable for the hackathon, while the long-term licensing and business model will be decided after user testing and the initial release.
Trainy began with a simple question on a Shinkansen platform: why can't tracking a train feel this good? The next step is turning that answer into something travelers can use around the world.
Built With
- cloudflare
- codex
- crashlytics
- firebase
- github
- ios
- mapkit
- ns-api
- odpt
- rest
- swift
- swiftui
- typescript
- xcode
Log in or sign up for Devpost to join the conversation.