Files
Maycon Santos 58ccfd245d Bug/network type switch crash swift logs (#39)
* Add completion handler usage when restarting client

* Fix UI stuck in connecting state during network type switches

  Message:
  When switching between wifi and cellular, the Go engine fires rapid state
  changes (connecting → disconnecting → connected) that confused the UI
  animation state machine, causing it to get stuck in "Connecting..." state.

  Changes:
  - Add isRestarting flag to suppress intermediate state updates during restart
  - Add recovery path in playDisconnectingLoop when engine reconnects
  - Add status polling guard to prevent concurrent fetchData calls
  - Add AppLogger for unified Swift logging to shared container
  - Update share logs to export both engine and app log files

* add build and test workflows and handle missing firebase files

* update jobs and docs with build script

* fix paths

* fix PeerCard.swift ref

* fix comments

* remove network extension refs

* Removed PBXBuildFile references

* use newer xcode

* handle airplane mode

* remove xcode selection

* address comments

* use macos-14

* use a subdirectory

* use a subdirectory in test

* improve reliability when handling auth

* Removed Firebase initialization in networkextension

* remove mac designed for ipad support from gui

* fix readme version

* add popoverPresentationController when running on ipad

* Uses temporary directory instead of Documents

* Removed the misleading comment about onConnected()

* Added fallback to Documents directory when App Group is unavailable

* Added tests

* Added workflow permissions

* handle potential nil in tests

* handle airplane mode freezes

* fix comment

* update airplane mode handler

* build 9

* remove print and reduce max log app size to 100kb

* fix comments

* build 10

* minor UI bugfixes (#40)

* Remove setting of statusDetailsValid to false on handleServerChanged()

This variable is used to show the splash screen until the app successfully starts polling,
then it's set to true and and it's never set to false again afterwards.

* Change DispatchQueue.global usage for Task.detached to stop network extension

Gets rid of warning: Main actor-isolated property 'networkExtensionAdapter'
can not be referenced from a Sendable closure.

The ViewModel class is marked @MainActor, which means networkExtensionAdapter
is isolated to the main actor. Accessing it from a DispatchQueue.global closure
(which runs on a background thread) violates Swift's actor isolation rules.

* Manually stop and start polling when navigating to and from ServerView

Choosing to Change Server creates a dialog that when clicked on the Confirm
button, it stops the client connection.  This is handled via the handleServerChanged method.

When app starts, though, it polls in a loop for the current state of the connection via startPollingDetails.
It is overwriting the states manually assigned inside handleServerChanged.
This commit manually turns off the polling when handleServerChanged is called, and turns it back on
after user navigates away from ServerView.

* Resolve network switch nitpicks (#41)

* Offload file IO to backgroud thread while sharing logs

* Use atomic property wrapper on isFetchingStatus to avoid racing conditions

* Add timer to fetchData to make sure isFetchingStatus is set to false

In the odd case the completionHandler for session.sendProviderMessage never gets called

* Replace usage of print with AppLogger.shared.log in PacketTunnelProvider

* Set currentNetworkType = nil on network restore

So the first post-restore path update won't incorrectly skip a type-change restart due to stale state

* Add debouncing mechanism on networkChange

To keep restarts from happening in succession if the network interfaces available
switch back and forth too quicky

networkChangeWorkItem (DispatchWorkItem) is used for this and it iss stopped
and cleard in these scenarios:
network loss, tunnel stop, rapid network type change (the latter, before potentially
scheduling a new one)

* Add startMonitoringNetworkChanges inside the monitor queue async block

To maintain order of state resetting before monitoring resumes on restart

* Use atomic property wrappers on clientState and isRestarting

Avoids potential cross-queue reads: handleNetworkChange runs
on monitorQueue (changing isRestarting) and adapter.clientState
 is written from ConnectionListener but read on monitorQueue

* Add lock to stopCompletionHandler to avoid race condition

This handler is accessed in multiple queues without synchronization:
Set in stop() - called from monitorQueue (via restartClient in PacketTunnelProvider)
Read/cleared in notifyStopCompleted() - called from:
    - DispatchQueue.global() (timeout)
    - ConnectionListener.onDisconnected() (Go SDK callback queue)

* Use guard and preconditionFailure if it fails to call NetBirdSDKNewPreferences

* Extract common getfilePath method usage when getting config and state files

File names were added to constants in GlobalConstants
Attempts to use applicationSupportDirectory as fallback if app group is not available;
Then documentDirectory, and finally, temporaryDirectory as last resort

* Use FileManager.copyItem instead of loading logs into memory

Log and delete tempDir if rootViewController isn't found

* Add timeout for restartClient

If in 30s the stopCompletionHandler (adapter.start()) isn't called,
the timeout will reset the adapter's isRestarting and packetTunnelProvider's
isRestartInProgress flags to false so the UI can recover from supressed state
updates

* Add explicit dismissal of ChangeServerAlert when clicking Confirm button

* Set currentNetworkType to nil on stopTunnel

if the extension process stays alive, the next handleNetworkChange
may treat the “first satisfied path” as a transition rather than initialization.

* Dispatch networkChangeWorkItem cancellation to monitorQueue

* Add guard usage when attempting to create log files

For rotateLogsIfNeeded and clearLogs so that isReady is marked as false
in case calling FileManager.default.createFile returns nil; on exception,
isReady is set to false and fileHandle is set to nil so that log creation doesn't
fail silently.

* Revert "Set currentNetworkType = nil on network restore"

This reverts commit eadb036e63.

* Resolve handling network changes when it switches between wifi and cellular (#42)

* Offload file IO to backgroud thread while sharing logs

* Use atomic property wrapper on isFetchingStatus to avoid racing conditions

* Add timer to fetchData to make sure isFetchingStatus is set to false

In the odd case the completionHandler for session.sendProviderMessage never gets called

* Replace usage of print with AppLogger.shared.log in PacketTunnelProvider

* Set currentNetworkType = nil on network restore

So the first post-restore path update won't incorrectly skip a type-change restart due to stale state

* Add debouncing mechanism on networkChange

To keep restarts from happening in succession if the network interfaces available
switch back and forth too quicky

networkChangeWorkItem (DispatchWorkItem) is used for this and it iss stopped
and cleard in these scenarios:
network loss, tunnel stop, rapid network type change (the latter, before potentially
scheduling a new one)

* Add startMonitoringNetworkChanges inside the monitor queue async block

To maintain order of state resetting before monitoring resumes on restart

* Use atomic property wrappers on clientState and isRestarting

Avoids potential cross-queue reads: handleNetworkChange runs
on monitorQueue (changing isRestarting) and adapter.clientState
 is written from ConnectionListener but read on monitorQueue

* Add lock to stopCompletionHandler to avoid race condition

This handler is accessed in multiple queues without synchronization:
Set in stop() - called from monitorQueue (via restartClient in PacketTunnelProvider)
Read/cleared in notifyStopCompleted() - called from:
    - DispatchQueue.global() (timeout)
    - ConnectionListener.onDisconnected() (Go SDK callback queue)

* Use guard and preconditionFailure if it fails to call NetBirdSDKNewPreferences

* Extract common getfilePath method usage when getting config and state files

File names were added to constants in GlobalConstants
Attempts to use applicationSupportDirectory as fallback if app group is not available;
Then documentDirectory, and finally, temporaryDirectory as last resort

* Use FileManager.copyItem instead of loading logs into memory

Log and delete tempDir if rootViewController isn't found

* Add timeout for restartClient

If in 30s the stopCompletionHandler (adapter.start()) isn't called,
the timeout will reset the adapter's isRestarting and packetTunnelProvider's
isRestartInProgress flags to false so the UI can recover from supressed state
updates

* Add explicit dismissal of ChangeServerAlert when clicking Confirm button

* Set currentNetworkType to nil on stopTunnel

if the extension process stays alive, the next handleNetworkChange
may treat the “first satisfied path” as a transition rather than initialization.

* Dispatch networkChangeWorkItem cancellation to monitorQueue

* Add guard usage when attempting to create log files

For rotateLogsIfNeeded and clearLogs so that isReady is marked as false
in case calling FileManager.default.createFile returns nil; on exception,
isReady is set to false and fileHandle is set to nil so that log creation doesn't
fail silently.

* Revert "Set currentNetworkType = nil on network restore"

This reverts commit eadb036e63.

* Bump version (0.0.14.12)

* Add Network.NWPath info logs on handleNetworkChange

* Remove early return on network reestablishment

To allow network type change detection to run after network restoration.
Sometimes NWPathMonitor reports NWPath with status != .satistied on network change.

* Remove setting currentNetworkType to nil when network goes down

To maintain the previously used network type and correctly detect a network change
when the connection is reestablished with another type to trigger a restart

* Bump version (0.0.14.13)

* Add logic to restart engine if recovering from network unavailable

* Bump version (0.0.14.14)

* Bump version (0.0.14.15)

This version is packaged with NetBirdSDK that executes
engine.go start's method with extra logs and execution
of PopulateNetbirdConfig in a goroutine

---------

Co-authored-by: Diego Romar <doromaraujo+github@gmail.com>
2025-12-18 18:06:10 +01:00
..