Skip to content

Add a timeout to network-info fetch to prevent blank web view and lost connection on launch#5017

Open
bgoncal wants to merge 4 commits into
mainfrom
webview-regression
Open

Add a timeout to network-info fetch to prevent blank web view and lost connection on launch#5017
bgoncal wants to merge 4 commits into
mainfrom
webview-regression

Conversation

@bgoncal

@bgoncal bgoncal commented Jul 9, 2026

Copy link
Copy Markdown
Member

Summary

Since URL resolution moved to async (#4942 / #4953), every path that picks the active URL awaits a fresh NEHotspotNetwork.fetchCurrent. That completion handler occasionally never fires (seen at launch/foreground, before location services are ready), and there was no timeout, so refreshNetworkInformation() / activeURL() would hang indefinitely. Everything awaiting them then stalls at once:

  • the web view load hangs with loadActiveURLIfNeededInProgress stuck true, so no later event retries it — blank page that never reloads
  • the WebSocket never connects (the cold-connect Task never reaches connectAPI)
  • the account row shows no user (currentUser never completes)

This matches the intermittent reports of a blank web view that won't reload, servers listed with no username, and no WebSocket connection.

Fix: bound performNetworkStateFetch() with a 3s timeout. If the fetch doesn't return in time, keep the last-known network state instead of hanging (or overwriting it with an empty one). Callers always make progress, and the normal launch/foreground events re-drive the connection once network info is available. Catalyst reads network info synchronously and is unaffected.

Screenshots

Not applicable — no user-facing/UI change.

Link to pull request in Documentation repository

Documentation: home-assistant/companion.home-assistant#

Any other notes

  • 3s is a safety cap only; fetchCurrent normally returns well under a second.
  • On timeout the previous value is preserved rather than cleared, so a slow fetch can't flip the app to the wrong URL.

URL resolution now awaits NEHotspotNetwork.fetchCurrent, whose completion
occasionally never fires at launch/foreground (before location services are
ready). With no timeout, the network-info refresh and every activeURL()
awaiting it hang indefinitely, leaving a blank web view that never reloads,
no WebSocket connection, and no user in the account row.

Bound the fetch with a 3s timeout; on timeout keep the last-known network
state instead of hanging or clearing it.
Copilot AI review requested due to automatic review settings July 9, 2026 13:47
@github-actions

github-actions Bot commented Jul 9, 2026

Copy link
Copy Markdown
Contributor

⚠️ Unused L10n strings detected

Found 3 unused localization strings in the codebase.

Click to see details
Parsing Strings.swift...
Found 1904 L10n strings

Reading all Swift source code...
Read 6385422 characters of Swift code

Checking for unused strings...
Checked 100/1904 strings...
Checked 200/1904 strings...
Checked 300/1904 strings...
Checked 400/1904 strings...
Checked 500/1904 strings...
Checked 600/1904 strings...
Checked 700/1904 strings...
Checked 800/1904 strings...
Checked 900/1904 strings...
Checked 1000/1904 strings...
Checked 1100/1904 strings...
Checked 1200/1904 strings...
Checked 1300/1904 strings...
Checked 1400/1904 strings...
Checked 1500/1904 strings...
Checked 1600/1904 strings...
Checked 1700/1904 strings...
Checked 1800/1904 strings...
Checked 1900/1904 strings...

================================================================================
UNUSED STRINGS REPORT
================================================================================

Found 3 unused strings:


CAMERAPLAYER:
  - L10n.CameraPlayer.Talkback.microphoneDenied
    Key: camera_player.talkback.microphone_denied
    Line: 820

CONNECTION:
  - L10n.Connection.Permission.InternalUrl.body1
    Key: connection.permission.internal_url.body1
    Line: 1370
  - L10n.Connection.Permission.InternalUrl.body2
    Key: connection.permission.internal_url.body2
    Line: 1372

================================================================================
Total unused: 3
================================================================================

================================================================================
Copy-paste these keys into the "Lokalise: Delete Keys" workflow (keys input):
================================================================================
camera_player.talkback.microphone_denied,connection.permission.internal_url.body1,connection.permission.internal_url.body2

To remove them, run the
Lokalise: Delete Keys
workflow — it deletes the keys from Lokalise and opens a PR removing them from
Localizable.strings and regenerating Strings.swift. Copy-paste these keys into the keys input:

camera_player.talkback.microphone_denied,connection.permission.internal_url.body1,connection.permission.internal_url.body2

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds a bounded timeout to the iOS network-info fetch so URL resolution can’t hang indefinitely during launch/foreground, preventing downstream stalls (blank WKWebView, no WS connect, missing user row) when NEHotspotNetwork.fetchCurrent never calls back.

Changes:

  • Wraps NEHotspotNetwork.fetchCurrent in a 3-second timeout within ConnectivityWrapper.performNetworkStateFetch().
  • On timeout, preserves the cached/last-known NetworkState instead of blocking or replacing it with empty data.

Comment thread Sources/Shared/Environment/ConnectivityWrapper.swift Outdated
Comment thread Sources/Shared/Environment/ConnectivityWrapper.swift Outdated
bgoncal and others added 3 commits July 9, 2026 17:56
Address review feedback: move the fetch timeout into the shared
fetchNetworkState() behind an injectable raw-fetch closure and a
configurable timeout, and include the duration in the timeout log so it
is actionable when tuning. Adds tests covering the timeout-preserves-
last-known-state path and the normal completion path.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
ConnectivitySensorTests leaves Current.connectivity.currentNetworkState
overridden, so the new timeout tests relying on the default routing hit
that stale closure. Call fetchNetworkState() directly instead.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants