Targets the real problem: an app (e.g. Roblox) getting closed/disconnected by
iOS once it's in the background. iOS won't let one app keep another alive —
except that a process a debugger is attached to is not suspended on
backgrounding (the same reason an Xcode-run app keeps executing in the
background). This uses that.
- JITEnableContext.keepAppAlive: launch the app suspended, vAttach (which sets
CS_DEBUGGED so JIT is on and marks the process debugger-owned), disable its
memory limit to reduce background jetsam kills, send a raw continue so it
runs, then hold the debug connection open with a heartbeat until the user
stops it — then interrupt + detach so the app keeps running on its own. The
hold is a single-threaded sleep-poll (no blocking socket reads, no concurrent
handle access), so it's cleanly cancellable.
- BackgroundAliveManager + HoldToken: own one active hold, keep StikDebug itself
alive via DebugKeepAliveLease for the duration, publish the active app name.
- UI: an experimental Settings toggle ("Hold App Alive in Background"); when on,
tapping an app in Home holds it alive instead of a normal one-shot JIT, and a
banner with a Stop button appears while a hold is active.
Caveats (documented in-app): experimental and unverified on-device; requires
StikDebug to stay running, so it's best-effort not bulletproof; cannot revive
an app iOS already killed; higher battery use.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Qznh32XffG6vwFLMcwm1Mq
When you enable JIT for an app and switch to it (e.g. a game), StikDebug
gets backgrounded. If iOS suspends it, the debug connection it holds — and
the marco/polo heartbeat that keeps that connection trusted — goes idle, the
device tears it down, and the app loses its session after a while. The app
also assumed Wi-Fi even though the tunnel rides LocalDevVPN and works on
cellular.
Session survival (backgrounding):
- DebugKeepAliveLease now forces the silent-audio and background-location
keep-alive on for the whole debug session, regardless of the user's
keep-alive toggles, so a session survives being switched out even if those
toggles are off.
- Stop the background-task expiration handler from tearing the whole
keep-alive down. It used to call invalidate() (killing audio/location) the
moment the ~30s assertion elapsed; now it just renews the assertion while
the silent-audio keep-alive continues to sustain background execution, so a
long backgrounded session keeps running instead of dropping.
- Play an inaudible non-zero signal instead of digital silence so iOS does
not treat the audio session as idle and reclaim it (suspending the app).
Cellular support:
- Add NetworkPathMonitor (NWPathMonitor) so the app observes the real path
(Wi-Fi, cellular, or VPN) instead of assuming Wi-Fi.
- Retry the tunnel connect with backoff so transient cellular failures
recover instead of erroring, skipping permanent errors (bad/missing pairing
file, invalid target IP).
- Reconnect the tunnel on network-path changes (Wi-Fi<->cellular handoff).
- Remove the false "Wi-Fi required" wording from the connection alerts and
the README.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Qznh32XffG6vwFLMcwm1Mq
Long routes took minutes to prepare (or appeared stuck) because of three
compounding costs, each now fixed:
- Overpass fetch: the query covered the route's whole bounding box, so a
long or diagonal route pulled every speed-limited road in a huge
rectangle. Replace it with an 'around' corridor query over a
downsampled route line (75 m spacing, capped at 700 points), so the
payload scales with route length, not bounding area. POST the query
(corridor strings outgrow GET URLs) with an explicit 30 s timeout so a
slow server can no longer stall route prep indefinitely — failures
fall back to profile/ETA pacing as before.
- Speed-limit matching: nearestSpeedLimit rescanned every OSM way
segment for every route segment (O(route x ways), tens of millions of
distance checks on long routes). Add RouteSpeedIndex, a grid-bucketed
spatial index (~440 m cells) probed via its 3x3 neighborhood; every
search radius is well below one cell, so results are unchanged. The
bus-stop approach check and the dwell snapping pass (previously
O(stops x samples)) use the same bucketing.
- Route densification: 10 m sampling made a 300 km route produce 30k+
points on the main thread. Sampling now widens adaptively past a 25k
point cap; playback interpolates per tick within segments anyway, so
smoothness is unaffected.
Previously only OSM nodes tagged highway=bus_stop were fetched and drawn,
which misses most stops in regions that use the newer public_transport
schema (platform / stop_position with bus=yes). Query and decode all of
these tag combinations so bus mode surfaces the full set of stops instead
of the sparse few tagged the legacy way.
The bus stops fetched from OpenStreetMap for bus-mode pacing/dwell were
only used to compute playback timing and never drawn. Surface them from
the prefetch result into view state and render them as orange bus markers
whenever the Bus speed profile is active.
- New toolbar menu on the location simulator map to switch between
Standard, Satellite, and Hybrid map types
- Traffic overlay toggle for Standard/Hybrid modes
- Points-of-interest mode: All Places, Transit Stops (surfaces bus
stops), or Hidden
- Selections persist across launches via AppStorage
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CUJbpUQiRUbjiTiYA4v7UK
Adds a SpeedProfile picker to route simulation with fixed paces for
walking, jogging, and cycling; road-speed-limit-based driving; a
capped bus profile that slows on approach to and dwells at Overpass
bus_stop nodes; and a custom km/h speed entered by the user. Also
extends OverpassResponse.Element with lat/lon so bus stop nodes
(which report coordinates directly rather than via geometry) can be
parsed.