Zoltan Papp 76177e3e5c Backport single exit node selection (#151)
* Bump netbird-core: backport single exit-node enforcement

Points the submodule at backport/single-exit-node (844881f5c), which
carries the exit-node exclusivity reconcile onto v0.74.0 so at most one
exit node stays selected.

* Enforce single exit-node selection in the routes UI

Activating an exit node now deselects every other selected exit node
(mirroring the desktop/Wails toggleExitNode behaviour), so 0.0.0.0/0 can't
stay pinned to the previously selected peer while the UI shows only the new
one. Siblings are deselected before the new node is selected so the core
drops the old exit node before adding the new one. Non-exit routes are
untouched.

* Bump netbird-core: atomic exit-node reconciliation

Points the submodule at 415eb59ef, which moves the single exit-node
reconcile into one locked RouteSelector method so a concurrent deselect-all
can no longer interleave and get undone.

* fix(ios): sequence exit-node switch and reconcile route selection

selectRoute fired the new selectRoutes before the sibling deselectRoutes
round-trips completed. Since both go through independent async
sendProviderMessage calls, the select could race the deselects and the
core would drop the just-added exit node, leaving 0.0.0.0/0 pinned to the
old peer.

Deselect every selected exit-node sibling first, track them in a
DispatchGroup, and only send the select once all deselects complete. After
the select, reconcile the optimistic UI with the core's real state via
GetRoutes so a rejected change reverts the toggle instead of leaving a
stale selection (Select/Deselect messages don't report their applied
result).

* fix(ios): guarantee deselectRoutes completion and refresh sibling toggle

deselectRoutes returned without calling its completion on the nil-session,
send-throw, and UTF-8 branches. selectRoute balances a DispatchGroup around
each sibling deselect, so a missed completion stranded the group and the
follow-up select/reconcile for the new exit node never ran. Invoke the
completion on every exit path.

RoutesSelectionInfo.selected is a plain (non-@Published) property, so mutating
a sibling emitted no change and its RouteCard toggle stayed on until the next
getRoutes reconcile. Send objectWillChange before deselecting the sibling so
the toggle flips off immediately, matching the mutually-exclusive exit-node UX.

* Update submodule
2026-07-03 18:05:05 +02:00
2024-02-28 19:22:16 +01:00
2024-02-28 19:22:16 +01:00
2024-02-28 19:22:16 +01:00
2024-02-28 19:22:16 +01:00


Start using NetBird at netbird.io
See Documentation
Join our Slack channel


NetBird iOS & tvOS Client

The NetBird iOS/tvOS client allows connections from mobile devices running iOS 14.0+ and Apple TV running tvOS 17.0+ to private resources in the NetBird network.

Install

You can download and install the app from the App Store:

Download on the App Store

Screenshots

mainscreen peer-overview menu

Code structure

The code is divided into 4 parts:

  • The main netbird Go code, included as a git submodule at /netbird-core from the NetBird repo, compiled into an xcframework. This contains most of the client logic.
  • The network extension /NetbirdNetworkExtension that is running in the background where the compiled Go code is executed.
  • The UI and app code under /NetBird
  • The /NetbirdKit/NetworkExtensionAdapter that controls the extension and builds the link between UI and extension

Requirements

  • iOS 14.0+ / tvOS 17.0+
  • Xcode 16.1+
  • Go (version determined by the netbird submodule's go.mod)
  • gomobile (for iOS builds)
  • gomobile-netbird (for tvOS builds — NetBird's fork with tvOS support)

gomobile

For iOS-only builds, install the standard gomobile:

go install golang.org/x/mobile/cmd/gomobile@latest

gomobile-netbird

For tvOS builds, install gomobile-netbird, NetBird's fork of gomobile that adds tvOS support. See: https://github.com/netbirdio/gomobile-tvos-fork

go install github.com/netbirdio/gomobile-tvos-fork/cmd/gomobile-netbird@latest

Run locally

The main netbird Go code is included as a git submodule at netbird-core/.

git clone --recursive https://github.com/netbirdio/ios-client.git
cd ios-client

If you already cloned without --recursive:

git submodule update --init --recursive

Build the XCFramework for iOS:

./build-go-lib.sh

Or for tvOS (includes iOS, iOS Simulator, tvOS, and tvOS Simulator targets):

./build-go-lib.sh --tvos

You can also pass an explicit version:

./build-go-lib.sh 0.2.0
./build-go-lib.sh --tvos 0.2.0

Open the Xcode project, and we are ready to go.

Updating the netbird submodule

To update to a newer netbird version:

cd netbird-core
git fetch --tags
git checkout v0.x.y   # or any branch/commit
cd ..
git add netbird-core
git commit -m "update netbird submodule to v0.x.y"

Running on iOS Device

Note: The app cannot run in the iOS simulator. To test the app, a physical device needs to be connected to Xcode via cable and set as the run destination.

Running on Apple TV

Note: The app cannot run in the tvOS simulator. To test the app, a physical device running tvOS 17.0 or later needs to be paired with Xcode.

Firebase Configuration (Optional)

The app supports Firebase for analytics and crash reporting. To enable it, add your GoogleService-Info.plist file to the project root. The app will work without Firebase configuration.

Other project repositories

NetBird project is composed of multiple repositories:

Languages
Swift 98%
C 1.1%
Shell 0.5%
Objective-C 0.4%