Live feed: FR24 primary, OpenSky fallback

OpenSky's free anonymous tier has sparse ground coverage — at DAL it
returned a single airborne aircraft when there were 3+ SWA jets
visibly parked on the apron. FR24's feed.js aggregates ASDE-X, MLAT,
and multiple community ADS-B feeds and reliably surfaces ground
aircraft at major airports. We now query FR24 first and fall back to
OpenSky only when FR24 errors.

FR24's payload also carries departure + arrival IATA + flight number
+ aircraft type + tail number inline, so we shortcut the
route-explorer schedule lookup in the detail sheet: a new
`LiveAircraft.Enrichment` struct holds those fields, and the
ResolvedRoute cascade gains a `.fromFR24` first-tier case that uses
them directly. The `typeCode` and `airlineICAO` computed properties
prefer enrichment values over the AircraftDatabase / callsign-prefix
heuristics — this also fixes the case where FR24 callsigns use the
IATA carrier ("AA0013") which our 3-letter-prefix derivation would
have rejected.

OpenSky still owns trail polylines and recent-flights history; only
the live position fetch swapped sources.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
Trey T
2026-05-27 07:52:56 -05:00
parent 68c60ec087
commit 92bc6ed52e
8 changed files with 352 additions and 23 deletions
+4
View File
@@ -59,6 +59,7 @@
LVBB000BBBB000BBBB000001 /* LiveFilterPicker.swift in Sources */ = {isa = PBXBuildFile; fileRef = LVBB000BBBB000BBBB000002 /* LiveFilterPicker.swift */; };
LVCC000CCCC000CCCC000001 /* aircraftDB.json in Resources */ = {isa = PBXBuildFile; fileRef = LVCC000CCCC000CCCC000002 /* aircraftDB.json */; };
LVDD000DDDD000DDDD000001 /* LocationService.swift in Sources */ = {isa = PBXBuildFile; fileRef = LVDD000DDDD000DDDD000002 /* LocationService.swift */; };
LVEE000EEEE000EEEE000001 /* FR24Client.swift in Sources */ = {isa = PBXBuildFile; fileRef = LVEE000EEEE000EEEE000002 /* FR24Client.swift */; };
/* End PBXBuildFile section */
/* Begin PBXContainerItemProxy section */
@@ -126,6 +127,7 @@
LVBB000BBBB000BBBB000002 /* LiveFilterPicker.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LiveFilterPicker.swift; sourceTree = "<group>"; };
LVCC000CCCC000CCCC000002 /* aircraftDB.json */ = {isa = PBXFileReference; lastKnownFileType = text.json; path = aircraftDB.json; sourceTree = "<group>"; };
LVDD000DDDD000DDDD000002 /* LocationService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LocationService.swift; sourceTree = "<group>"; };
LVEE000EEEE000EEEE000002 /* FR24Client.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FR24Client.swift; sourceTree = "<group>"; };
/* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */
@@ -246,6 +248,7 @@
LV7700007777000077770002 /* OpenSkyCredentials.swift */,
LVAA000AAAA000AAAA000002 /* AircraftDatabase.swift */,
LVDD000DDDD000DDDD000002 /* LocationService.swift */,
LVEE000EEEE000EEEE000002 /* FR24Client.swift */,
);
path = Services;
sourceTree = "<group>";
@@ -418,6 +421,7 @@
LVAA000AAAA000AAAA000001 /* AircraftDatabase.swift in Sources */,
LVBB000BBBB000BBBB000001 /* LiveFilterPicker.swift in Sources */,
LVDD000DDDD000DDDD000001 /* LocationService.swift in Sources */,
LVEE000EEEE000EEEE000001 /* FR24Client.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};