Widget links maps:// directly so CarPlay can route to Apple Maps
- The widget now uses the native maps://?daddr= scheme for taps in both sizes. In CarPlay there is no containing app to relay through, but since Apple Maps IS a CarPlay app, the system may route the tap straight to Maps on the car display (WWDC25: widgets can launch apps in CarPlay). - iOS keeps working via onOpenURL: the app now forwards maps:// URLs (if the Home Screen delivers them to the app), plus legacy fuelboard:// and http://maps.apple.com links from older cached timelines. - No public API exists for a widget to detect CarPlay context, so the single maps:// link serves both; the app-side forward is the iOS safety net. 35 tests pass.
This commit is contained in:
@@ -9,18 +9,35 @@ struct FuelBoardApp: App {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// WidgetKit widgets can only open their containing app, so a widget tap
|
/// Handles deep links that end up in the app. Widget taps arrive here in
|
||||||
/// delivers a `fuelboard://directions?lat=..&lng=..` URL here instead of
|
/// two cases:
|
||||||
/// opening Apple Maps itself. Forward it to the native Maps directions
|
/// - legacy/cached widget timelines using the `fuelboard://` relay, or
|
||||||
/// URL (`maps://?daddr=`), which is what the app's own rows use.
|
/// - the Home Screen delivering a `maps://` URL to the containing app
|
||||||
|
/// instead of opening Maps directly (the system's choice on iOS).
|
||||||
|
/// Either way we forward to the native Apple Maps directions URL. The
|
||||||
|
/// `http://maps.apple.com` form is also handled, from very old timelines.
|
||||||
|
/// In CarPlay the widget uses the same `maps://` URL, which routes to
|
||||||
|
/// Apple Maps without ever launching this app.
|
||||||
private func handleOpenURL(_ url: URL) {
|
private func handleOpenURL(_ url: URL) {
|
||||||
guard url.scheme == "fuelboard",
|
guard let components = URLComponents(url: url, resolvingAgainstBaseURL: false),
|
||||||
url.host == "directions",
|
let items = components.queryItems
|
||||||
let components = URLComponents(url: url, resolvingAgainstBaseURL: false),
|
|
||||||
let lat = components.queryItems?.first(where: { $0.name == "lat" })?.value.flatMap(Double.init),
|
|
||||||
let lng = components.queryItems?.first(where: { $0.name == "lng" })?.value.flatMap(Double.init),
|
|
||||||
let mapsURL = URL(string: "maps://?daddr=\(lat),\(lng)&t=d")
|
|
||||||
else { return }
|
else { return }
|
||||||
|
|
||||||
|
// Preferred: maps://?daddr=lat,lng&t=d (also covers http://maps.apple.com).
|
||||||
|
if let daddr = items.first(where: { $0.name == "daddr" })?.value {
|
||||||
|
openMaps(daddr: daddr)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Legacy relay: fuelboard://directions?lat=..&lng=..
|
||||||
|
if let lat = items.first(where: { $0.name == "lat" })?.value.flatMap(Double.init),
|
||||||
|
let lng = items.first(where: { $0.name == "lng" })?.value.flatMap(Double.init) {
|
||||||
|
openMaps(daddr: "\(lat),\(lng)")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private func openMaps(daddr: String) {
|
||||||
|
guard let mapsURL = URL(string: "maps://?daddr=\(daddr)&t=d") else { return }
|
||||||
UIApplication.shared.open(mapsURL)
|
UIApplication.shared.open(mapsURL)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,9 +5,13 @@ import AppIntents
|
|||||||
// FuelBoard widget — petrol stations near you.
|
// FuelBoard widget — petrol stations near you.
|
||||||
// systemMedium: top 3-4 stations with price + distance, each row opens Maps
|
// systemMedium: top 3-4 stations with price + distance, each row opens Maps
|
||||||
// systemSmall: single station, whole widget opens Maps
|
// systemSmall: single station, whole widget opens Maps
|
||||||
// Taps deep-link to Apple Maps directions (maps://?daddr=).
|
// Taps deep-link to Apple Maps directions (maps://?daddr=). The widget uses
|
||||||
// Note: on the home screen Link opens Maps directly. Inside CarPlay the widget
|
// the native Maps scheme (not a custom app scheme) so that CarPlay can route
|
||||||
// renders but can't launch Maps (widgets can only launch their own CarPlay app).
|
// the tap straight to Apple Maps — which IS a CarPlay app — without needing
|
||||||
|
// the containing app to launch. On the Home Screen the system either opens
|
||||||
|
// Maps directly or delivers the URL to FuelBoard, whose onOpenURL forwards it
|
||||||
|
// (and also still handles legacy fuelboard:// and http://maps.apple.com links
|
||||||
|
// from older cached timelines).
|
||||||
//
|
//
|
||||||
// Each widget instance is configured INDEPENDENTLY via its own App Intent
|
// Each widget instance is configured INDEPENDENTLY via its own App Intent
|
||||||
// (long-press → Edit Widget): fuel type (Unleaded/Premium/Diesel) and sort
|
// (long-press → Edit Widget): fuel type (Unleaded/Premium/Diesel) and sort
|
||||||
@@ -261,7 +265,7 @@ struct FuelPriceWidgetView: View {
|
|||||||
.foregroundStyle(.secondary)
|
.foregroundStyle(.secondary)
|
||||||
}
|
}
|
||||||
ForEach(entry.stations.prefix(3)) { station in
|
ForEach(entry.stations.prefix(3)) { station in
|
||||||
Link(destination: station.widgetDirectionsURL ?? URL(string: "fuelboard://")!) {
|
Link(destination: station.widgetDirectionsURL ?? URL(string: "maps://")!) {
|
||||||
HStack(spacing: 8) {
|
HStack(spacing: 8) {
|
||||||
Text(station.name)
|
Text(station.name)
|
||||||
.font(.caption.weight(.semibold))
|
.font(.caption.weight(.semibold))
|
||||||
|
|||||||
@@ -159,13 +159,16 @@ struct FuelStation: Identifiable, Codable, Equatable {
|
|||||||
URL(string: "maps://?daddr=\(lat),\(lng)&t=d")
|
URL(string: "maps://?daddr=\(lat),\(lng)&t=d")
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Widget tap URL. WidgetKit widgets can ONLY open their containing app:
|
/// Widget tap URL — the native Maps scheme (`maps://`). From the iOS Home
|
||||||
/// the system hands this URL to FuelBoard via `onOpenURL`, which then
|
/// Screen the system may either (a) open Maps directly, or (b) deliver the
|
||||||
/// forwards to Apple Maps. So the widget link uses our own registered
|
/// URL to the containing app, where FuelBoardApp.onOpenURL forwards it to
|
||||||
/// `fuelboard://` scheme rather than `maps://` (which would arrive in the
|
/// Maps. In CarPlay there is no containing app, but because `maps://`
|
||||||
/// app and be silently dropped).
|
/// targets Apple Maps — an app that IS in CarPlay — the system may route
|
||||||
|
/// the tap straight to the car display, bypassing the app entirely. This
|
||||||
|
/// is the only scheme with a chance of working in CarPlay; the old
|
||||||
|
/// `fuelboard://` relay is kept as a handled fallback in onOpenURL.
|
||||||
var widgetDirectionsURL: URL? {
|
var widgetDirectionsURL: URL? {
|
||||||
URL(string: "fuelboard://directions?lat=\(lat)&lng=\(lng)")
|
URL(string: "maps://?daddr=\(lat),\(lng)&t=d")
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Name of the bundled brand logo asset, or nil if unknown.
|
/// Name of the bundled brand logo asset, or nil if unknown.
|
||||||
|
|||||||
Reference in New Issue
Block a user