Conversation
Locking the screen or leaving the app drops the SSH session. Nothing in the app hangs up — `enterBackground()` keeps the shell, the parser state and the credentials, and `checkRetainedConnection` reuses the same PTY on return when it answers. The session dies because iOS suspends the process, and a suspended process cannot service its socket. The app was not asking for any time before that happened. There is no `beginBackgroundTask` in it, so the system is free to suspend the moment the scene goes to the background. It now holds an assertion, which is worth about thirty seconds and covers the cases most likely to be behind the report: - glancing at another app and coming straight back - pressing the power button and unlocking a few seconds later - pulling down a notification Those survive today only by luck. With the assertion held, the probe on return finds a live transport instead of a dead one. Taken only when a session is connected or being checked. Asking the system to stay awake with nothing to keep alive is wasted battery and the kind of thing that gets noticed. This does not make the app stay connected in the background, and is not meant to. No entitlement iOS grants an SSH client holds a TCP session open indefinitely — the categories are audio, location, VoIP, external accessory and `NEAppPushProvider`, and a terminal is none of them. `README.md` already points at a remote multiplexer for continuity across a real disconnection, and #39 is where the one legitimate route to not being suspended is tracked. The assertion is injectable because the failure worth testing is not taking one — it is failing to give one back, which is how iOS kills an app. Four tests cover the paths that matter: not asking with nothing connected, giving it back on return, giving it back from the expiry handler, and not double-ending it afterwards. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Locking the screen or leaving the app drops the SSH session.
The app is not the one hanging up
enterBackground()keeps the shell, the parser state and the credentials, and on returncheckRetainedConnectionreuses the same PTY when the transport answers. The session dies because iOS suspends the process, and a suspended process cannot service its socket.The app was not asking for any time before that happened. There is no
beginBackgroundTaskanywhere in it, so the system is free to suspend the moment the scene goes to the background.What this buys
An assertion worth about thirty seconds, which covers the cases most likely to be behind the report:
Those survive today only by luck. With the assertion held, the probe on return finds a live transport rather than a dead one.
Taken only when a session is
connectedorchecking. Asking the system to stay awake with nothing to keep alive is wasted battery and the kind of thing that gets noticed.What this is not
This does not keep the app connected in the background, and is not meant to. No entitlement iOS grants an SSH client holds a TCP session open indefinitely — the categories are audio, location, VoIP, external accessory and
NEAppPushProvider, and a terminal is none of them.README.mdalready points at a remote multiplexer for continuity across a real disconnection, and #39 tracks the one legitimate route to not being suspended at all.Thirty seconds is a real improvement to the common case and nothing more. It is worth being plain about which one the report was about.
Tests
The assertion is injectable because the failure worth testing is not taking one — it is failing to give one back, which is how iOS kills an app.
nothingIsAskedForWhenNoSessionIsConnectedtheAssertionIsGivenBackWhenTheAppReturnstheAssertionIsGivenBackWhenTheTimeRunsOutgoingToTheBackgroundTwiceHoldsOneAssertionAll four pass. Simulators shut down after the run.
🤖 Generated with Claude Code