I'm trying to trying to get Rx (received packet byte count) and Tx (transmitted packet byte count) in MacOS Swift app that uses TunnelKit to create a WireGuard connection to a VPN.
I've verified that I am connected to my VPN and am getting data. However, I'm not getting the Rx and Tx values back at all - here's my code; I'm using the SystemConfiguration framework to try get the data for the connected VPN.
Here is where I am trying to get the VPN statistics data from via the SystemConfiguration
framework
#import <Foundation/Foundation.h>
#import <SystemConfiguration/SystemConfiguration.h>
NS_ASSUME_NONNULL_BEGIN
@interface VPNConnectionStatisticsManager : NSObject
- (SCNetworkConnectionRef _Nullable)initializeVPNConnectionWithServiceID:(NSString *)serviceID;
@end
NS_ASSUME_NONNULL_END
#import <Foundation/Foundation.h>
#import "VPNConnectionStatisticsManager.h"
@implementation VPNConnectionStatisticsManager
- (SCNetworkConnectionRef _Nullable)initializeVPNConnectionWithServiceID:(NSString *)serviceID {
SCNetworkConnectionContext context = {0, NULL, NULL, NULL, NULL};
SCNetworkConnectionRef connection = SCNetworkConnectionCreateWithServiceID(NULL, (__bridge CFStringRef)serviceID, ConnectionStatusChanged, &context);
return connection;
}
static void ConnectionStatusChanged(SCNetworkConnectionRef connection, SCNetworkConnectionStatus status, void *info) {
NSLog(@"VPN status changed: %d", (int)status);
}
@end
I changed my code to make sure that VPNConnectionStatisticsManager
is not being recreated on every call. Have also included my view code that shows this in context:
class VPNStatusViewModel: ObservableObject {
var vpnStatusRefreshTimer: Timer?
var vpnConnection: SCNetworkConnection?
private let vpnConnectionStatisticsManager = VPNConnectionStatisticsManager()
func initializeVPNConnection() {
if let serviceID = findMeterVPNServiceID(),
let vpnConnectionRef = vpnConnectionStatisticsManager.initializeVPNConnection(withServiceID: serviceID) {
vpnConnection = vpnConnectionRef.takeRetainedValue()
}
}
func getVPNData() -> (rx: String, tx: String) {
var rx: String = "N/A"
var tx: String = "N/A"
if let vpnConnection = vpnConnection,
let statsDict = SCNetworkConnectionCopyStatistics(vpnConnection) as? [String: Any],
let vpnDict = statsDict["VPN"] as? [String: Any] {
if let rxInt = vpnDict["BytesIn"] as? Int, let txInt = vpnDict["BytesOut"] as? Int {
let formatter = ByteCountFormatter()
formatter.allowedUnits = [.useBytes, .useKB, .useMB]
rx = formatter.string(fromByteCount: Int64(rxInt))
tx = formatter.string(fromByteCount: Int64(txInt))
}
}
return (rx, tx)
}
func stopVPNStatusTimer() {
vpnStatusRefreshTimer?.invalidate()
vpnStatusRefreshTimer = nil
}
func releaseVPNConnection() {
vpnConnection = nil
}
}
struct VPNStatusPane: View {
@ObservedObject var appDelegate: AppDelegate
@StateObject private var viewModel = VPNStatusViewModel()
@State var connectedSince: String
@State var rxBytes: String = "0"
@State var txBytes: String = "0"
init(appDelegate: AppDelegate) {
self.appDelegate = appDelegate
self._connectedSince = State(initialValue: appDelegate.vpnConnectedSince)
}
var body: some View {
VStack(alignment: .leading) {
VStack(alignment: .leading, spacing: 10) {
HypeneatedText(label: "Rx bytes", value: rxBytes)
HypeneatedText(label: "Tx bytes", value: txBytes)
HypeneatedText(label: "Connected for", value: connectedSince)
}
.padding(.top, 15)
Spacer()
}
.onAppear {
viewModel.initializeVPNConnection()
updateVPNData()
startVPNStatusTimer()
}
.onDisappear {
viewModel.stopVPNStatusTimer()
viewModel.releaseVPNConnection()
}
.frame(maxWidth: .infinity, alignment: .topLeading)
}
private func startVPNStatusTimer() {
viewModel.vpnStatusRefreshTimer = Timer.scheduledTimer(withTimeInterval: 1.0, repeats: true) { [self] _ in
if self.appDelegate.vpnIsConnected {
self.appDelegate.updateConnectionTime()
}
self.connectedSince = self.appDelegate.vpnConnectedSince
self.updateVPNData()
}
}
private func updateVPNData() {
let vpnData = viewModel.getVPNData()
rxBytes = vpnData.rx
txBytes = vpnData.tx
}
}
It appears the intention is to discuss or review a piece of Objective-C code related to a VPNConnectionStatisticsManager class; however, the actual code has not been provided in your message. To proceed in a helpful manner, I'll describe how such a class and methods might be implemented based on your description.
In Objective-C, a class named VPNConnectionStatisticsManager might be declared in a header file (.h) like this:
VPNConnectionStatisticsManager.h
And the implementation in an implementation file (.m) might be as follows:
VPNConnectionStatisticsManager.m
This basic implementation simply demonstrates how you might create a class method to initialize a VPN connection using the SCNetworkConnectionCreateWithServiceID function, and how to log the connection status changes using a callback function.
Keep in mind that:
If you have the actual code or specific concerns or questions about the implementation, please share that for a more accurate and detailed response.