Make the rules inspectable
Show the chosen bounds, their distances, stopovers, fare choices and reasons for splitting.
INDEPENDENT PROJECT · NATIVE iOS
Turning complex flight rules into clear decisions.
I designed and built an iPhone and iPad app that explains how an Aeroplan reward itinerary fits together—from routing and points to connection times and cached seat availability.
TestFlight 1.0.0 (9) · Released October 27, 2026
01 / THE PROBLEM
A plausible-looking route can still fail a distance rule, require multiple pricing bounds, contain an impossible connection, or have no confirmed reward space. I wanted the app to explain those distinctions instead of hiding them behind a single points number.
Show the chosen bounds, their distances, stopovers, fare choices and reasons for splitting.
Separate a published flight, cached reward availability, a verified connection and an unresolved rule.
Let people change dates, cabin codes, layouts and languages without losing a draft or applying an old response.
02 / THE EXPERIENCE
The map gives the trip a shape. The editor makes details precise. The result explains what the rules actually allow.
Search by flight or route, keep local airport times, select cabins and booking codes, and retain manual overrides.
See the points estimate broken into bounds, with stopovers, fare choices and routing decisions alongside it.
A confirmed short connection blocks the total. Unresolved missing or conflicting information stays explicitly unverified.
Actual native app screenshots from build 9. Select any image to inspect it at a larger size.
03 / REAL TESTED ROUTES
A bound is a separately priced one-way portion of a trip. Explore saved development scenarios and regression fixtures with recorded outcomes.
Fixed-chart estimates for the recorded cabin and fare choices. The analysis groups a supplied route; it does not search for the cheapest flight or promise current award availability.
The distance limit depends on the endpoints of the complete bound. A partial route can fail, then become valid once a later destination changes the direct-distance baseline. Treating every failed prefix as a permanent split produced the wrong partition.
I replaced that decision with suffix dynamic programming: evaluate each complete candidate of up to six flights, minimize the number of bounds, and prefer a longer earlier bound when counts tie. The ten-flight regression changed from 375,000 to the intended 310,000 points.
This is a routing policy, not a minimum-points optimizer. A different valid four-bound partition totals 290,000 points; the specified tie-break intentionally selects the 310,000-point arrangement.
INSIDE THE ROUTING ENGINE
A small excerpt of the current partitioning implementation in Xcode. It evaluates complete candidates, retains the best suffix, and keeps invalid single flights visible for diagnosis instead of dropping them.
Development view captured October 29, 2026. The same original Swift excerpt appears in both language versions.
04 / ENGINEERING
I separated the domain model, on-device state, provider integrations and presentation so a screen change does not become a pricing-rule change.
MapKit globe, editor, result views and charts. Layout and language are presentation choices.
A MainActor store owns drafts, saved itineraries, travelers and fare selections.
Swift models analyze complete bounds, chart pricing, stopovers and connection requirements.
Separate service actors provide schedules and cached reward data without replacing the local rules.
Connection rules can depend on airport pairs, operating or marketing carriers, terminals, flight-number ranges and dates. Carrier-specific selectors take precedence over station defaults.
Build 9 distinguishes cabin-level availability from the actual Air Canada booking code selected in the editor. P inventory must not appear as I inventory.
FlightAware schedules, Seats.aero availability and Landline timetables have separate service actors. Identical in-flight requests are shared; manual input remains usable when lookups fail.
Each editor opening receives a fresh session ID, stable across resizing. Old callbacks cannot mutate a newly reopened editor—even in an A → B → A sequence.
The engine sums great-circle distances in floating point and compares the complete bound against twice its endpoint distance. Rounding belongs at display and chart lookup—not inside the feasibility check.
The displayed charts and the itinerary engine share one resolver. Approved imported cells take precedence; explicit blanks stay blank. An unapproved range never becomes a guessed midpoint.
DESIGNING FOR AVAILABLE SPACE
The phone uses a movable flight panel over the map. On iPad, a floating flight island leaves the globe visible. Results and charts choose columns from their actual presented width, and large text returns them to a single column.
I also implemented named panel actions, adjustable controls, Reduce Motion and Reduce Transparency behavior. Selected layouts were checked in Simulator; this is not a claim of exhaustive accessibility certification.
¹ The offline database is a dated snapshot of station standards and collected carrier exceptions, not exhaustive worldwide coverage. Runtime data is JSON; a separate SQLite export supports research and validation.
05 / THE INTERFACE, UP CLOSE
Real build-9 screens. English and Chinese show matching views; switching this page’s language changes every app screenshot as well.
SMALL DETAILS, SHARED FOUNDATIONS
An airport is more than a map pin.Airport codes, region metadata, coordinates and IANA time zones support the map, routing rules and local-time calculations. The detail sheet makes that context visible next to the traveler’s own flights.06 / VALIDATION & DELIVERY
I tested the domain rules separately from the UI, checked service responses and cache behavior with fixtures, then verified selected native flows in Simulator and shipped a signed build through TestFlight.
Three native languages share stable business values and saved itinerary data. Build 9 contains 816 keys in each language catalog; localization checks cover missing keys and formatting.
The figures here are separate, scoped check suites recorded for build 9. They are not added into a headline total, and automated layout checks do not substitute for every physical-device or accessibility test.
LET’S TALK ABOUT THE WORK
I’m happy to discuss the routing engine, the UI decisions, or engineering opportunities. Public TestFlight testing is also open—contact me for an invitation.
Independent personal project, not affiliated with Air Canada or Aeroplan. Fixed-chart estimates exclude taxes and dynamic pricing; schedules and cached availability are not booking guarantees.