Skip to content
Open
Show file tree
Hide file tree
Changes from 5 commits
Commits
Show all changes
34 commits
Select commit Hold shift + click to select a range
a1e2af0
Add completion handler usage when restarting client
doromaraujo Dec 12, 2025
08c206e
Fix UI stuck in connecting state during network type switches
mlsmaycon Dec 13, 2025
a3f6cb4
add build and test workflows and handle missing firebase files
mlsmaycon Dec 13, 2025
a52f02e
update jobs and docs with build script
mlsmaycon Dec 13, 2025
cf6f3a2
fix paths
mlsmaycon Dec 13, 2025
ae948b9
fix PeerCard.swift ref
mlsmaycon Dec 13, 2025
da6bbe9
fix comments
mlsmaycon Dec 14, 2025
f22e1db
remove network extension refs
mlsmaycon Dec 14, 2025
4251fc7
Removed PBXBuildFile references
mlsmaycon Dec 14, 2025
28ddad6
use newer xcode
mlsmaycon Dec 14, 2025
08093a7
handle airplane mode
mlsmaycon Dec 14, 2025
62659ac
remove xcode selection
mlsmaycon Dec 14, 2025
aada818
address comments
mlsmaycon Dec 14, 2025
303ea9d
use macos-14
mlsmaycon Dec 14, 2025
8743371
use a subdirectory
mlsmaycon Dec 14, 2025
4a54da7
use a subdirectory in test
mlsmaycon Dec 14, 2025
a30ea8e
improve reliability when handling auth
mlsmaycon Dec 14, 2025
0a16812
Removed Firebase initialization in networkextension
mlsmaycon Dec 14, 2025
ed7ec21
remove mac designed for ipad support from gui
mlsmaycon Dec 14, 2025
bd521b0
fix readme version
mlsmaycon Dec 14, 2025
4177849
add popoverPresentationController when running on ipad
mlsmaycon Dec 14, 2025
6defc9f
Uses temporary directory instead of Documents
mlsmaycon Dec 14, 2025
bf1225c
Removed the misleading comment about onConnected()
mlsmaycon Dec 14, 2025
0aee71b
Added fallback to Documents directory when App Group is unavailable
mlsmaycon Dec 14, 2025
d69a8cf
Added tests
mlsmaycon Dec 14, 2025
242a463
Added workflow permissions
mlsmaycon Dec 14, 2025
5f83261
handle potential nil in tests
mlsmaycon Dec 14, 2025
b369f73
handle airplane mode freezes
mlsmaycon Dec 14, 2025
1028f7c
fix comment
mlsmaycon Dec 14, 2025
50b0165
update airplane mode handler
mlsmaycon Dec 14, 2025
e935811
build 9
mlsmaycon Dec 14, 2025
a07b82d
remove print and reduce max log app size to 100kb
mlsmaycon Dec 14, 2025
dc70b66
fix comments
mlsmaycon Dec 15, 2025
e24dce4
build 10
mlsmaycon Dec 15, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
75 changes: 75 additions & 0 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
name: Build

on:
push:
branches: [main]
pull_request:

concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true

jobs:
build:
name: Build iOS App
runs-on: macos-14

steps:
- name: Checkout ios-client
uses: actions/checkout@v4

- name: Checkout netbird
uses: actions/checkout@v4
with:
repository: netbirdio/netbird
path: netbird

- name: Setup Go
uses: actions/setup-go@v5
with:
go-version: '1.24'
cache-dependency-path: netbird/go.sum

- name: Install gomobile
run: go install golang.org/x/mobile/cmd/gomobile@v0.0.0-20251113184115-a159579294ab

- name: Build NetBirdSDK xcframework
run: ./build-go-lib.sh $PWD/netbird

- name: Select Xcode
run: sudo xcode-select -s /Applications/Xcode_16.1.app

- name: Install xcpretty
run: gem install xcpretty

- name: Resolve Swift packages
run: |
xcodebuild -resolvePackageDependencies \
-project NetBird.xcodeproj \
-scheme NetBird

- name: Build iOS App
run: |
set -o pipefail
xcodebuild build \
-project NetBird.xcodeproj \
-scheme NetBird \
-destination 'generic/platform=iOS' \
-configuration Debug \
CODE_SIGNING_ALLOWED=NO \
CODE_SIGNING_REQUIRED=NO \
CODE_SIGN_IDENTITY="" \
| xcpretty --color

- name: Build Network Extension
run: |
set -o pipefail
xcodebuild build \
-project NetBird.xcodeproj \
-scheme NetbirdNetworkExtension \
-destination 'generic/platform=iOS' \
-configuration Debug \
CODE_SIGNING_ALLOWED=NO \
CODE_SIGNING_REQUIRED=NO \
CODE_SIGN_IDENTITY="" \
| xcpretty --color
60 changes: 60 additions & 0 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
name: Test

on:
push:
branches: [main]
pull_request:

concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true

jobs:
test:
name: Build for Simulator
runs-on: macos-14

steps:
- name: Checkout ios-client
uses: actions/checkout@v4

- name: Checkout netbird
uses: actions/checkout@v4
with:
repository: netbirdio/netbird
path: netbird

- name: Setup Go
uses: actions/setup-go@v5
with:
go-version: '1.24'
cache-dependency-path: netbird/go.sum

- name: Install gomobile
run: go install golang.org/x/mobile/cmd/gomobile@v0.0.0-20251113184115-a159579294ab

- name: Build NetBirdSDK xcframework
run: ./build-go-lib.sh $PWD/netbird

- name: Select Xcode
run: sudo xcode-select -s /Applications/Xcode_16.1.app

- name: Install xcpretty
run: gem install xcpretty

- name: Resolve Swift packages
run: |
xcodebuild -resolvePackageDependencies \
-project NetBird.xcodeproj \
-scheme NetBird
- name: Build for Simulator
run: |
set -o pipefail
xcodebuild build \
-project NetBird.xcodeproj \
-scheme NetBird \
-destination 'platform=iOS Simulator,name=iPhone 16' \
-configuration Debug \
CODE_SIGNING_ALLOWED=NO \
| xcpretty --color
28 changes: 17 additions & 11 deletions NetBird.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -91,13 +91,15 @@
50E608202A7979D600BAF09B /* SideDrawer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 50E6081F2A7979D600BAF09B /* SideDrawer.swift */; };
50E608242A79966600BAF09B /* AboutView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 50E608232A79966600BAF09B /* AboutView.swift */; };
50E608262A79968500BAF09B /* AdvancedView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 50E608252A79968500BAF09B /* AdvancedView.swift */; };
978FC4702EEDF167002D0EB8 /* AppLogger.swift in Sources */ = {isa = PBXBuildFile; fileRef = 978FC46F2EEDF167002D0EB8 /* AppLogger.swift */; };
978FC4712EEDF167002D0EB8 /* AppLogger.swift in Sources */ = {isa = PBXBuildFile; fileRef = 978FC46F2EEDF167002D0EB8 /* AppLogger.swift */; };
F1258DE22ED4EE5000C0D205 /* ServerViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = F1258DE12ED4EE4900C0D205 /* ServerViewModel.swift */; };
F1258DEA2ED7B7D600C0D205 /* Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = F1258DE92ED7B7D200C0D205 /* Extensions.swift */; };
F1B292052EDE5610001D91B8 /* JustifiedText.swift in Sources */ = {isa = PBXBuildFile; fileRef = F1B292042EDE5608001D91B8 /* JustifiedText.swift */; };
F1B292072EE0AC2A001D91B8 /* EnvVarPackager.swift in Sources */ = {isa = PBXBuildFile; fileRef = F1B292062EE0AC25001D91B8 /* EnvVarPackager.swift */; };
F1B292082EE0AC2A001D91B8 /* EnvVarPackager.swift in Sources */ = {isa = PBXBuildFile; fileRef = F1B292062EE0AC25001D91B8 /* EnvVarPackager.swift */; };
F1B2920A2EE0BC46001D91B8 /* GlobalConstants.swift in Sources */ = {isa = PBXBuildFile; fileRef = F1B292092EE0BC40001D91B8 /* GlobalConstants.swift */; };
F1B2920B2EE0BC46001D91B8 /* GlobalConstants.swift in Sources */ = {isa = PBXBuildFile; fileRef = F1B292092EE0BC40001D91B8 /* GlobalConstants.swift */; };
F1258DE22ED4EE5000C0D205 /* ServerViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = F1258DE12ED4EE4900C0D205 /* ServerViewModel.swift */; };
F1258DEA2ED7B7D600C0D205 /* Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = F1258DE92ED7B7D200C0D205 /* Extensions.swift */; };
F1B292052EDE5610001D91B8 /* JustifiedText.swift in Sources */ = {isa = PBXBuildFile; fileRef = F1B292042EDE5608001D91B8 /* JustifiedText.swift */; };
/* End PBXBuildFile section */

/* Begin PBXContainerItemProxy section */
Expand Down Expand Up @@ -184,11 +186,12 @@
50E6081F2A7979D600BAF09B /* SideDrawer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SideDrawer.swift; sourceTree = "<group>"; };
50E608232A79966600BAF09B /* AboutView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AboutView.swift; sourceTree = "<group>"; };
50E608252A79968500BAF09B /* AdvancedView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AdvancedView.swift; sourceTree = "<group>"; };
F1B292062EE0AC25001D91B8 /* EnvVarPackager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EnvVarPackager.swift; sourceTree = "<group>"; };
F1B292092EE0BC40001D91B8 /* GlobalConstants.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GlobalConstants.swift; sourceTree = "<group>"; };
978FC46F2EEDF167002D0EB8 /* AppLogger.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppLogger.swift; sourceTree = "<group>"; };
F1258DE12ED4EE4900C0D205 /* ServerViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ServerViewModel.swift; sourceTree = "<group>"; };
F1258DE92ED7B7D200C0D205 /* Extensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Extensions.swift; sourceTree = "<group>"; };
F1B292042EDE5608001D91B8 /* JustifiedText.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = JustifiedText.swift; sourceTree = "<group>"; };
F1B292062EE0AC25001D91B8 /* EnvVarPackager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EnvVarPackager.swift; sourceTree = "<group>"; };
F1B292092EE0BC40001D91B8 /* GlobalConstants.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GlobalConstants.swift; sourceTree = "<group>"; };
/* End PBXFileReference section */

/* Begin PBXFrameworksBuildPhase section */
Expand Down Expand Up @@ -312,6 +315,7 @@
50C727EA2A82479B006E898D /* NetbirdKit */ = {
isa = PBXGroup;
children = (
978FC46F2EEDF167002D0EB8 /* AppLogger.swift */,
F1B292092EE0BC40001D91B8 /* GlobalConstants.swift */,
F1B292062EE0AC25001D91B8 /* EnvVarPackager.swift */,
50245A292A7BDB590034792B /* Preferences.swift */,
Expand Down Expand Up @@ -578,6 +582,7 @@
buildActionMask = 2147483647;
files = (
50CD81632AD0595E00CF830B /* DNSManager.swift in Sources */,
978FC4702EEDF167002D0EB8 /* AppLogger.swift in Sources */,
50C5D3102BDD96CF003159BE /* RoutesSelectionDetails.swift in Sources */,
50C727ED2A824C10006E898D /* NetBirdAdapter.swift in Sources */,
50245A572A80431C0034792B /* PacketTunnelProvider.swift in Sources */,
Expand Down Expand Up @@ -611,6 +616,7 @@
502455BF2A79B4500034792B /* SolidButton.swift in Sources */,
50BB17412C30239400518BCA /* RouteCard.swift in Sources */,
505344B92C3EFE4C00223065 /* TransparentGradientButton.swift in Sources */,
978FC4712EEDF167002D0EB8 /* AppLogger.swift in Sources */,
50E608202A7979D600BAF09B /* SideDrawer.swift in Sources */,
50216D932ACB2488009574C9 /* NetworkExtensionAdapter.swift in Sources */,
509CCD6C2BE90D0E00B7C2D8 /* PeerTabView.swift in Sources */,
Expand Down Expand Up @@ -837,7 +843,7 @@
CLANG_ENABLE_MODULES = YES;
CODE_SIGN_ENTITLEMENTS = NetBird/NetBird.entitlements;
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 5;
CURRENT_PROJECT_VERSION = 7;
DEVELOPMENT_ASSET_PATHS = "";
DEVELOPMENT_TEAM = TA739QLA7A;
ENABLE_PREVIEWS = YES;
Expand Down Expand Up @@ -866,12 +872,12 @@
"$(PROJECT_DIR)/WireGuardKitGo/.tmp/wireguard-go-bridge/goroot/src/go/internal/gccgoimporter/testdata",
"$(PROJECT_DIR)/WireGuardKitGo/.tmp/wireguard-go-bridge",
);
MARKETING_VERSION = 0.0.13;
MARKETING_VERSION = 0.0.14;
PRODUCT_BUNDLE_IDENTIFIER = io.netbird.app;
PRODUCT_NAME = "$(TARGET_NAME)";
SUPPORTED_PLATFORMS = "iphoneos iphonesimulator";
SUPPORTS_MACCATALYST = NO;
SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO;
SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = YES;
SWIFT_EMIT_LOC_STRINGS = YES;
SWIFT_OBJC_BRIDGING_HEADER = "NetBird-Bridging-Header.h";
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
Expand All @@ -889,7 +895,7 @@
CLANG_ENABLE_MODULES = YES;
CODE_SIGN_ENTITLEMENTS = NetBird/NetBird.entitlements;
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 5;
CURRENT_PROJECT_VERSION = 7;
DEVELOPMENT_ASSET_PATHS = "";
DEVELOPMENT_TEAM = TA739QLA7A;
ENABLE_PREVIEWS = YES;
Expand Down Expand Up @@ -918,12 +924,12 @@
"$(PROJECT_DIR)/WireGuardKitGo/.tmp/wireguard-go-bridge/goroot/src/go/internal/gccgoimporter/testdata",
"$(PROJECT_DIR)/WireGuardKitGo/.tmp/wireguard-go-bridge",
);
MARKETING_VERSION = 0.0.13;
MARKETING_VERSION = 0.0.14;
PRODUCT_BUNDLE_IDENTIFIER = io.netbird.app;
PRODUCT_NAME = "$(TARGET_NAME)";
SUPPORTED_PLATFORMS = "iphoneos iphonesimulator";
SUPPORTS_MACCATALYST = NO;
SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO;
SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = YES;
SWIFT_EMIT_LOC_STRINGS = YES;
SWIFT_OBJC_BRIDGING_HEADER = "NetBird-Bridging-Header.h";
SWIFT_VERSION = 5.0;
Expand Down
6 changes: 4 additions & 2 deletions NetBird/Source/App/NetBirdApp.swift
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,10 @@ import FirebasePerformance
class AppDelegate: NSObject, UIApplicationDelegate {
func application(_ application: UIApplication,
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey : Any]? = nil) -> Bool {
let options = FirebaseOptions(contentsOfFile: Bundle.main.path(forResource: "GoogleService-Info", ofType: "plist")!)
FirebaseApp.configure(options: options!)
if let path = Bundle.main.path(forResource: "GoogleService-Info", ofType: "plist"),
let options = FirebaseOptions(contentsOfFile: path) {
FirebaseApp.configure(options: options)
}
return true
}
}
Expand Down
64 changes: 37 additions & 27 deletions NetBird/Source/App/Views/AdvancedView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -187,43 +187,53 @@ struct AdvancedView: View {
}

func shareButtonTapped() {
let fileManager = FileManager.default
guard let groupURL = fileManager.containerURL(forSecurityApplicationGroupIdentifier: "group.io.netbird.app") else {
print("Failed to retrieve the group URL")
guard let documentsDir = getDocumentsDirectory() else {
AppLogger.shared.log("Failed to get documents directory")
return
}

let logURL = groupURL.appendingPathComponent("logfile.log")
var filesToShare: [URL] = []

do {
let logData = try String(contentsOf: logURL, encoding: .utf8)
let fileName = "netbird-log.txt"
guard let filePath = getDocumentsDirectory()?.appendingPathComponent(fileName) else {
print("Failed to get file path")
return
// Export Go SDK logs
if let goLogURL = AppLogger.getGoLogFileURL() {
do {
let goLogData = try String(contentsOf: goLogURL, encoding: .utf8)
let goLogPath = documentsDir.appendingPathComponent("netbird-engine.log")
try goLogData.write(to: goLogPath, atomically: true, encoding: .utf8)
filesToShare.append(goLogPath)
} catch {
AppLogger.shared.log("Failed to read Go log data: \(error)")
}

}

// Export Swift logs
if let swiftLogURL = AppLogger.getLogFileURL() {
do {
try logData.write(to: filePath, atomically: true, encoding: .utf8)

let activityViewController = UIActivityViewController(activityItems: [filePath], applicationActivities: nil)

activityViewController.excludedActivityTypes = [
.assignToContact,
.saveToCameraRoll
]

if let windowScene = UIApplication.shared.connectedScenes.first as? UIWindowScene,
let rootViewController = windowScene.windows.first?.rootViewController {
rootViewController.present(activityViewController, animated: true, completion: nil)
}
let swiftLogData = try String(contentsOf: swiftLogURL, encoding: .utf8)
let swiftLogPath = documentsDir.appendingPathComponent("netbird-app.log")
try swiftLogData.write(to: swiftLogPath, atomically: true, encoding: .utf8)
filesToShare.append(swiftLogPath)
} catch {
print("Failed to write to file: \(error.localizedDescription)")
AppLogger.shared.log("Failed to read Swift log data: \(error)")
}
} catch {
print("Failed to read log data: \(error)")
}

guard !filesToShare.isEmpty else {
AppLogger.shared.log("No log files to share")
return
}

let activityViewController = UIActivityViewController(activityItems: filesToShare, applicationActivities: nil)

activityViewController.excludedActivityTypes = [
.assignToContact,
.saveToCameraRoll
]

if let windowScene = UIApplication.shared.connectedScenes.first as? UIWindowScene,
let rootViewController = windowScene.windows.first?.rootViewController {
rootViewController.present(activityViewController, animated: true, completion: nil)
}
}

func getDocumentsDirectory() -> URL? {
Expand Down
11 changes: 11 additions & 0 deletions NetBird/Source/App/Views/Components/CustomLottieView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -163,6 +163,17 @@ struct CustomLottieView: UIViewRepresentable {
guard let self = self else { return }
if self.extensionStatus == .disconnected {
self.playFadeOut(uiView: uiView, startFrame: self.disconnectingFadeOut.startFrame, endFrame: self.disconnectingFadeOut.endFrame, viewModel: viewModel, extensionStateText: "Disconnected")
} else if self.engineStatus == .connected && self.extensionStatus == .connected {
// Engine recovered to connected during internal restart (e.g., network switch)
// Extension never disconnected, so skip fade out and go directly to connected state
self.isPlaying = false
DispatchQueue.main.async {
viewModel.extensionStateText = "Connected"
viewModel.connectPressed = false
viewModel.disconnectPressed = false
viewModel.routeViewModel.getRoutes()
}
uiView.currentFrame = self.connectedFrame
} else {
playDisconnectingLoop(uiView: uiView, viewModel: viewModel)
}
Expand Down
Loading
Loading