History v2: everything — Wallet auto-prompt, age, track replay, share
Adds the deferred pieces from the v1 ship, plus a Mail Share
Extension target so the iOS share sheet picks up flight emails.
Track replay
- `LoggedFlight.icao24` field — populated from FR24 enrichment on
live-tap adds.
- HistoryDetailView's track query now fires for any flight younger
than 7 days that has an icao24, pulling the actual flown path
from OpenSky's /tracks/all endpoint. Falls back to a clean
great-circle arc otherwise.
Wallet auto-prompt
- RootView subscribes to WalletPassObserver.shared. When the user
adds a boarding pass to Apple Wallet, the observer's published
`pendingPass` flips and we present AddFlightView pre-filled with
the parsed origin / destination / flight # / date.
Airframe age + first-flight date
- `AirframeMetadataService` queries OpenSky's
/api/metadata/aircraft/icao/{icao24} endpoint. Caches results in
the existing `AirframeMetadata` SwiftData model so we never
re-fetch the same airframe twice. (jetphotos and planespotters
pages are both Cloudflare-gated; OpenSky's metadata API is the
cleanest free source.)
- HistoryDetailView fires the lookup on appear and persists the
result; the aircraft card already renders "Age" when a date is
cached.
Mail Share Extension
- New `FlightsShareExtension` Xcode target (app-extension product
type) built into the app bundle via an Embed Foundation
Extensions copy phase.
- `ShareViewController` (SLComposeServiceViewController) parses
shared text + URLs for flight-shaped codes ("AA 2178"), route
hints ("DFW → ORD"), and date strings.
- On Save, the extension builds a `flights://import?carrier=…&num=
…&dep=…&arr=…&date=…` URL and opens it via the responder-chain
openURL trick (Share Extensions can't access UIApplication
directly).
- Host app handles the URL via `.onOpenURL` in RootView, switches
to the History tab and presents AddFlightView prefilled.
- App now has an actual Info.plist (CFBundleURLTypes registered
for `flights://`); switched from GENERATE_INFOPLIST_FILE to
INFOPLIST_FILE for the app target.
If the dev portal hasn't registered bundle id
`com.flights.app.share` for the team, the signed archive will
fail. In that case the simpler URL-scheme path still works —
users can hit `flights://import?...` from a Shortcut.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
@@ -75,6 +75,9 @@
|
||||
HX0D000DDDD000DDDD000001 /* LifetimeStatsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = HX0D000DDDD000DDDD000002 /* LifetimeStatsView.swift */; };
|
||||
HX0E000EEEE000EEEE000001 /* HistoryRouteMapView.swift in Sources */ = {isa = PBXBuildFile; fileRef = HX0E000EEEE000EEEE000002 /* HistoryRouteMapView.swift */; };
|
||||
HX0F000FFFF000FFFF000001 /* YearInReviewView.swift in Sources */ = {isa = PBXBuildFile; fileRef = HX0F000FFFF000FFFF000002 /* YearInReviewView.swift */; };
|
||||
HX1000001000000010000001 /* AirframeMetadataService.swift in Sources */ = {isa = PBXBuildFile; fileRef = HX1000001000000010000002 /* AirframeMetadataService.swift */; };
|
||||
SX01000000000000000001A1 /* ShareViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = SX01000000000000000001B1 /* ShareViewController.swift */; };
|
||||
SX01000000000000000004A1 /* FlightsShareExtension.appex in Embed Foundation Extensions */ = {isa = PBXBuildFile; fileRef = SX01000000000000000003B1 /* FlightsShareExtension.appex */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; };
|
||||
/* End PBXBuildFile section */
|
||||
|
||||
/* Begin PBXContainerItemProxy section */
|
||||
@@ -85,8 +88,29 @@
|
||||
remoteGlobalIDString = E373C48C497D48D388BF7657;
|
||||
remoteInfo = Flights;
|
||||
};
|
||||
SX0100000000000000000DA1 /* PBXContainerItemProxy */ = {
|
||||
isa = PBXContainerItemProxy;
|
||||
containerPortal = 5418BEEAEFF644ADA7240CEA /* Project object */;
|
||||
proxyType = 1;
|
||||
remoteGlobalIDString = SX01000000000000000009A1;
|
||||
remoteInfo = FlightsShareExtension;
|
||||
};
|
||||
/* End PBXContainerItemProxy section */
|
||||
|
||||
/* Begin PBXCopyFilesBuildPhase section */
|
||||
SX0100000000000000000FA1 /* Embed Foundation Extensions */ = {
|
||||
isa = PBXCopyFilesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
dstPath = "";
|
||||
dstSubfolderSpec = 13;
|
||||
files = (
|
||||
SX01000000000000000004A1 /* FlightsShareExtension.appex in Embed Foundation Extensions */,
|
||||
);
|
||||
name = "Embed Foundation Extensions";
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
/* End PBXCopyFilesBuildPhase section */
|
||||
|
||||
/* Begin PBXFileReference section */
|
||||
04AC23D8748D42C9A7115FAC /* Airline.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Airline.swift; sourceTree = "<group>"; };
|
||||
0CD303E3EDCC4BF2BCF31722 /* AirportSearchField.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AirportSearchField.swift; sourceTree = "<group>"; };
|
||||
@@ -159,6 +183,10 @@
|
||||
HX0D000DDDD000DDDD000002 /* LifetimeStatsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LifetimeStatsView.swift; sourceTree = "<group>"; };
|
||||
HX0E000EEEE000EEEE000002 /* HistoryRouteMapView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HistoryRouteMapView.swift; sourceTree = "<group>"; };
|
||||
HX0F000FFFF000FFFF000002 /* YearInReviewView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = YearInReviewView.swift; sourceTree = "<group>"; };
|
||||
HX1000001000000010000002 /* AirframeMetadataService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AirframeMetadataService.swift; sourceTree = "<group>"; };
|
||||
SX01000000000000000001B1 /* ShareViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ShareViewController.swift; sourceTree = "<group>"; };
|
||||
SX01000000000000000002B1 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
|
||||
SX01000000000000000003B1 /* FlightsShareExtension.appex */ = {isa = PBXFileReference; explicitFileType = "wrapper.app-extension"; includeInIndex = 0; path = FlightsShareExtension.appex; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
/* End PBXFileReference section */
|
||||
|
||||
/* Begin PBXFrameworksBuildPhase section */
|
||||
@@ -176,6 +204,13 @@
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
SX01000000000000000007A1 /* Frameworks */ = {
|
||||
isa = PBXFrameworksBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
/* End PBXFrameworksBuildPhase section */
|
||||
|
||||
/* Begin PBXGroup section */
|
||||
@@ -250,10 +285,20 @@
|
||||
children = (
|
||||
8A3CB0CCC2524542AFB0D1D2 /* Flights.app */,
|
||||
T1000000000000000000003A /* FlightsTests.xctest */,
|
||||
SX01000000000000000003B1 /* FlightsShareExtension.appex */,
|
||||
);
|
||||
name = Products;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
SX01000000000000000005A1 /* FlightsShareExtension */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
SX01000000000000000001B1 /* ShareViewController.swift */,
|
||||
SX01000000000000000002B1 /* Info.plist */,
|
||||
);
|
||||
path = FlightsShareExtension;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
T1000000000000000000005A /* FlightsTests */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
@@ -293,6 +338,7 @@
|
||||
HX0500005555000055550002 /* StatsEngine.swift */,
|
||||
HX0600006666000066660002 /* CalendarFlightImporter.swift */,
|
||||
HX0700007777000077770002 /* WalletPassObserver.swift */,
|
||||
HX1000001000000010000002 /* AirframeMetadataService.swift */,
|
||||
);
|
||||
path = Services;
|
||||
sourceTree = "<group>";
|
||||
@@ -301,6 +347,7 @@
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
1D5A2C06B99046F3934D2E59 /* Flights */,
|
||||
SX01000000000000000005A1 /* FlightsShareExtension */,
|
||||
T1000000000000000000005A /* FlightsTests */,
|
||||
517CC07B82D949359C6CD4F5 /* Products */,
|
||||
);
|
||||
@@ -337,10 +384,12 @@
|
||||
A5535283EA784250AAF50064 /* Sources */,
|
||||
EB782B062CA144E2972778DE /* Frameworks */,
|
||||
6B9FCA84AAAA44529A95D7AC /* Resources */,
|
||||
SX0100000000000000000FA1 /* Embed Foundation Extensions */,
|
||||
);
|
||||
buildRules = (
|
||||
);
|
||||
dependencies = (
|
||||
SX0100000000000000000EA1 /* PBXTargetDependency */,
|
||||
);
|
||||
name = Flights;
|
||||
productName = Flights;
|
||||
@@ -364,6 +413,23 @@
|
||||
productReference = T1000000000000000000003A /* FlightsTests.xctest */;
|
||||
productType = "com.apple.product-type.bundle.unit-test";
|
||||
};
|
||||
SX01000000000000000009A1 /* FlightsShareExtension */ = {
|
||||
isa = PBXNativeTarget;
|
||||
buildConfigurationList = SX0100000000000000000CA1 /* Build configuration list for PBXNativeTarget "FlightsShareExtension" */;
|
||||
buildPhases = (
|
||||
SX01000000000000000006A1 /* Sources */,
|
||||
SX01000000000000000007A1 /* Frameworks */,
|
||||
SX01000000000000000008A1 /* Resources */,
|
||||
);
|
||||
buildRules = (
|
||||
);
|
||||
dependencies = (
|
||||
);
|
||||
name = FlightsShareExtension;
|
||||
productName = FlightsShareExtension;
|
||||
productReference = SX01000000000000000003B1 /* FlightsShareExtension.appex */;
|
||||
productType = "com.apple.product-type.app-extension";
|
||||
};
|
||||
/* End PBXNativeTarget section */
|
||||
|
||||
/* Begin PBXProject section */
|
||||
@@ -388,6 +454,7 @@
|
||||
projectRoot = "";
|
||||
targets = (
|
||||
E373C48C497D48D388BF7657 /* Flights */,
|
||||
SX01000000000000000009A1 /* FlightsShareExtension */,
|
||||
T1000000000000000000006A /* FlightsTests */,
|
||||
);
|
||||
};
|
||||
@@ -399,6 +466,11 @@
|
||||
target = E373C48C497D48D388BF7657 /* Flights */;
|
||||
targetProxy = T1000000000000000000002A /* PBXContainerItemProxy */;
|
||||
};
|
||||
SX0100000000000000000EA1 /* PBXTargetDependency */ = {
|
||||
isa = PBXTargetDependency;
|
||||
target = SX01000000000000000009A1 /* FlightsShareExtension */;
|
||||
targetProxy = SX0100000000000000000DA1 /* PBXContainerItemProxy */;
|
||||
};
|
||||
/* End PBXTargetDependency section */
|
||||
|
||||
/* Begin PBXResourcesBuildPhase section */
|
||||
@@ -413,6 +485,13 @@
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
SX01000000000000000008A1 /* Resources */ = {
|
||||
isa = PBXResourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
/* End PBXResourcesBuildPhase section */
|
||||
|
||||
/* Begin PBXSourcesBuildPhase section */
|
||||
@@ -483,6 +562,7 @@
|
||||
HX0D000DDDD000DDDD000001 /* LifetimeStatsView.swift in Sources */,
|
||||
HX0E000EEEE000EEEE000001 /* HistoryRouteMapView.swift in Sources */,
|
||||
HX0F000FFFF000FFFF000001 /* YearInReviewView.swift in Sources */,
|
||||
HX1000001000000010000001 /* AirframeMetadataService.swift in Sources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
@@ -494,6 +574,14 @@
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
SX01000000000000000006A1 /* Sources */ = {
|
||||
isa = PBXSourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
SX01000000000000000001A1 /* ShareViewController.swift in Sources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
/* End PBXSourcesBuildPhase section */
|
||||
|
||||
/* Begin XCBuildConfiguration section */
|
||||
@@ -506,13 +594,8 @@
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 1;
|
||||
DEVELOPMENT_TEAM = V3PF3M6B6U;
|
||||
GENERATE_INFOPLIST_FILE = YES;
|
||||
INFOPLIST_KEY_NSLocationWhenInUseUsageDescription = "Show your current location on the live flight map so you can quickly see aircraft overhead.";
|
||||
INFOPLIST_KEY_UIApplicationSceneManifest_Generation = YES;
|
||||
INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES;
|
||||
INFOPLIST_KEY_UILaunchScreen_Generation = YES;
|
||||
INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight";
|
||||
INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight";
|
||||
GENERATE_INFOPLIST_FILE = NO;
|
||||
INFOPLIST_FILE = Flights/Info.plist;
|
||||
LD_RUNPATH_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
"@executable_path/Frameworks",
|
||||
@@ -537,13 +620,8 @@
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 1;
|
||||
DEVELOPMENT_TEAM = V3PF3M6B6U;
|
||||
GENERATE_INFOPLIST_FILE = YES;
|
||||
INFOPLIST_KEY_NSLocationWhenInUseUsageDescription = "Show your current location on the live flight map so you can quickly see aircraft overhead.";
|
||||
INFOPLIST_KEY_UIApplicationSceneManifest_Generation = YES;
|
||||
INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES;
|
||||
INFOPLIST_KEY_UILaunchScreen_Generation = YES;
|
||||
INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight";
|
||||
INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight";
|
||||
GENERATE_INFOPLIST_FILE = NO;
|
||||
INFOPLIST_FILE = Flights/Info.plist;
|
||||
LD_RUNPATH_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
"@executable_path/Frameworks",
|
||||
@@ -646,6 +724,54 @@
|
||||
};
|
||||
name = Release;
|
||||
};
|
||||
SX0100000000000000000AA1 /* Debug */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 1;
|
||||
DEVELOPMENT_TEAM = V3PF3M6B6U;
|
||||
GENERATE_INFOPLIST_FILE = NO;
|
||||
INFOPLIST_FILE = FlightsShareExtension/Info.plist;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 17.0;
|
||||
LD_RUNPATH_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
"@executable_path/Frameworks",
|
||||
"@executable_path/../../Frameworks",
|
||||
);
|
||||
MARKETING_VERSION = 1.0;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.flights.app.share;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
SKIP_INSTALL = YES;
|
||||
SWIFT_EMIT_LOC_STRINGS = YES;
|
||||
SWIFT_VERSION = 5.0;
|
||||
TARGETED_DEVICE_FAMILY = "1,2";
|
||||
};
|
||||
name = Debug;
|
||||
};
|
||||
SX0100000000000000000BA1 /* Release */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 1;
|
||||
DEVELOPMENT_TEAM = V3PF3M6B6U;
|
||||
GENERATE_INFOPLIST_FILE = NO;
|
||||
INFOPLIST_FILE = FlightsShareExtension/Info.plist;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 17.0;
|
||||
LD_RUNPATH_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
"@executable_path/Frameworks",
|
||||
"@executable_path/../../Frameworks",
|
||||
);
|
||||
MARKETING_VERSION = 1.0;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.flights.app.share;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
SKIP_INSTALL = YES;
|
||||
SWIFT_EMIT_LOC_STRINGS = YES;
|
||||
SWIFT_VERSION = 5.0;
|
||||
TARGETED_DEVICE_FAMILY = "1,2";
|
||||
};
|
||||
name = Release;
|
||||
};
|
||||
/* End XCBuildConfiguration section */
|
||||
|
||||
/* Begin XCConfigurationList section */
|
||||
@@ -676,6 +802,15 @@
|
||||
defaultConfigurationIsVisible = 0;
|
||||
defaultConfigurationName = Release;
|
||||
};
|
||||
SX0100000000000000000CA1 /* Build configuration list for PBXNativeTarget "FlightsShareExtension" */ = {
|
||||
isa = XCConfigurationList;
|
||||
buildConfigurations = (
|
||||
SX0100000000000000000AA1 /* Debug */,
|
||||
SX0100000000000000000BA1 /* Release */,
|
||||
);
|
||||
defaultConfigurationIsVisible = 0;
|
||||
defaultConfigurationName = Release;
|
||||
};
|
||||
/* End XCConfigurationList section */
|
||||
};
|
||||
rootObject = 5418BEEAEFF644ADA7240CEA /* Project object */;
|
||||
|
||||
Reference in New Issue
Block a user