iOS: Extract network extension management into a separate file
This is the first step of the ViewController refactoring process described in #139
All the network extension stuff is now moved into a separate component and the ViewController's dependency graph changed from this
flowchart LR
viewController --> UIViewController
viewController --> UIButton
viewController --> UIImageView
viewController --> UILabel
viewController --> UserDefaults
viewController --> NotificationCenter
viewController --> MMWormhole
viewController --> NETunnelProviderManager
viewController --> NWPathMonitor
viewController --> NEVPNStatus
subgraph User Iinterface
UIButton
UIImageView
UILabel
UIViewController
end
subgraph Infrastructure
UserDefaults
NotificationCenter
MMWormhole
end
subgraph Networking
NETunnelProviderManager
NWPathMonitor
NEVPNStatus
end
to this:
flowchart RL
viewController --> UIViewController
viewController --> UIButton
viewController --> UIImageView
viewController --> UILabel
viewController --> UserDefaults
viewController --> NotificationCenter
viewController --> MMWormhole
TunnelManagerImpl --> NETunnelProviderManager
TunnelManagerImpl --> NEVPNStatus
TunnelManagerImpl -.-> TunnelManager["<TunnelManager>"]
TunnelManagerDelegate --> TunnelManagerState
viewController --> TunnelManager
viewController --> TunnelManagerDelegate
subgraph User Interface
UIButton
UIImageView
UILabel
UIViewController
end
subgraph Infrastructure
UserDefaults
NotificationCenter
MMWormhole
end
subgraph Network Extension
NETunnelProviderManager
NWPathMonitor
NEVPNStatus
end
subgraph NewNode
TunnelManager
TunnelManagerDelegate
TunnelManagerState
TunnelManagerImpl
end
There are some benefits of this approach:
-
ViewControllerdoes not depend on NetworkExtension, the dependency inversion principle applied -
TunnelManagercan be shared between iOS/macOS platforms without any change - We can have multiple implementations for the
TunnelManagerprotocol which allows us to focus on UI improvements on the simulator. Like so:
private lazy var tunnelManager: TunnelManager = {
#if targetEnvironment(simulator)
TunnelManagerSimulator(delegate: self)
#else
TunnelManagerImpl(delegate: self)
#endif
}()
- No need to use
NWPathMonitorsince we can rely onNWConnectionstate - Fixed bug when the connection fails after another VPN enabled
I'm very curious if NWConnection is really sufficient in place of NWPathMonitor. Path Monitor should notice changes independent of the the success of NWConnection. Which NWConnection are you referring to?
NEVPNConnection is more appropriate because it represents the state of your tunnel and the main app can handle all the state transitions while NWPathMonitor notifies you when the network changed and does not reflect the actual tunnel state
The NWPathMonitor is a more low-level API and does not depend on the NETunnelProviderManager's connection.
When NEVPNConnection is in a disconnected state the traffic goes directly (around the network extension) whether NewNode working or not