mirror of
https://github.com/netbirdio/ios-client.git
synced 2026-09-23 09:28:33 -07:00
* Show exit nodes right after connect without visiting another tab The exit node selector stayed on "No exit nodes available" after every connect until the user opened the Peers or Resources tab and came back. The extension reports the tunnel connected from onConnected, which the core fires once management and signal are up - before the engine is reachable and a few hundred milliseconds before the first network map arrives. The app reads the routes on that .connected transition, the core answers "not connected", and the extension swallowed that error into an empty route list. The app takes any answer from the core as the truth about the network map, so it cached zero routes. Nothing re-read them afterwards: the status poll only fetches status, and route reads happen on .connected and on a view's onAppear, which is why switching tabs "fixed" it. The extension log shows the same pair on every connect, a few milliseconds apart: onConnected: state=connected Error retrieving or encoding route selection details: not connected Two changes. getSelectRoutes now answers a core error with no payload, which the adapter already treats as a failed read that leaves the cache alone - an error is not zero routes. And the extension now tells the app when the network map moves: the core's onPeersListChanged fires at the end of every network map update, after the route manager has applied the routes, and on every route/peer state change, so the previously empty callback posts a Darwin notification (the same mechanism the app uses to announce MDM policy changes to the extension, in the other direction). Android's HomeFragment relies on the same event for the same reason. RoutesViewModel observes that notification and re-reads the routes, coalesced to one read per 300 ms window: the core announces a change per peer and per route, so a connect with dozens of peers produces dozens of notifications within seconds, and each read is an IPC round-trip that serialises the whole route list. One pending work item absorbs every notification until it fires; a change announced after the fire schedules the next, so nothing is lost. Optimistic selection writes and clearRoutes() retire in-flight and pending reads, so a read dispatched before a tap can no longer answer after it with the pre-tap selection; the mutation's own reconcile read is the one allowed to answer. * Drop network map notifications overtaken by a clear or a selection write scheduleNetworkMapRefresh hops to the main queue before it looks at the pending refresh, and that hop is a window. A notification still in transit when clearRoutes() or beginSelectionMutation() ran on main found the pending slot they had just emptied and scheduled a fresh read after the very invalidation that should have dropped it. After clearRoutes() that read is not harmless: the optimistic disconnect and the profile switch clear the routes while the extension is still winding down and still announcing peer changes, so the read can succeed and repopulate a list nothing clears again - the selector enabled over nodes the core can no longer apply. Capture a lock-guarded epoch when the notification arrives, on whatever thread the Darwin notify center delivers on, advance it in clearRoutes() and beginSelectionMutation(), and re-check it once the request reaches main. A notification that was valid when it arrived and is still valid on main schedules exactly as before.