Network Interface Statistics

This thread has been locked by a moderator.

For important background information, read Extra-ordinary Networking before reading this.

Share and Enjoy

Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"


Network Interface Statistics

One FAQ when it comes to network interfaces is “How do I get network interface statistics?” There are numerous variants of this:

  • Some folks ask about specific network interfaces: “How do I get cellular data usage?”

  • Some folks are interested in per-app statistics: “How do I get cellular data usage statistics for each app?” or “How do I get cellular data usage statistics for my app?”

  • Some folks only care about recent statistics: “How can I tell how much network data this operation generated?”

  • Some folks care about usage across restarts: “How do I get the cellular data usage shown in the Settings app on iOS?”

Most of these questions have no supported answers. However, there are a some supported techniques available. This post explains those techniques, and their limitations.

MetricKit

To get network usage for your app, use MetricKit. Specifically, look at the MXNetworkTransferMetric payload.

MetricKit has a number of design points:

  • You only get metrics for your app.

  • You get metrics periodically; you can’t monitor these statistics in real time.

Legacy Techniques

The getifaddrs routine returns rudimentary network interface statistics. See the getifaddrs man page and the struct if_data definition in <net/if_var.h>. Here’s an example of how you might use this:

func legacyNetworkInterfaceStatisticsForInterfaceNamed(_ name: String) -> LegacyNetworkInterfaceStatistics? {
    var addrList: UnsafeMutablePointer<ifaddrs>? = nil
    let err = getifaddrs(&addrList)
    // In theory we could check `errno` here but, honestly, what are gonna
    // do with that info?
    guard
        err >= 0,
        let first = addrList
    else { return nil }
    defer { freeifaddrs(addrList) }
    return sequence(first: first, next: { $0.pointee.ifa_next })
        .compactMap { addr in
            guard
                let nameC = addr.pointee.ifa_name,
                name == String(cString: nameC),
                let sa = addr.pointee.ifa_addr,
                sa.pointee.sa_family == AF_LINK,
                let data = addr.pointee.ifa_data
            else { return nil }
            return LegacyNetworkInterfaceStatistics(if_data: data.assumingMemoryBound(to: if_data.self).pointee)
        }
        .first
}

struct LegacyNetworkInterfaceStatistics {
    var packetsIn: UInt32   // ifi_ipackets
    var packetsOut: UInt32  // ifi_opackets
    var bytesIn: UInt32     // ifi_ibytes
    var bytesOut: UInt32    // ifi_obytes
}

extension LegacyNetworkInterfaceStatistics {
    init(if_data ifData: if_data) {
        self.packetsIn = ifData.ifi_ipackets
        self.packetsOut = ifData.ifi_opackets
        self.bytesIn = ifData.ifi_ibytes
        self.bytesOut = ifData.ifi_obytes
    }
}

This is a legacy interface. macOS inherited this API from its ancestor platforms, and iOS inherited it from macOS. That history means that the API has significant limitations:

  • The counters reset each time the device restarts.

  • The counters are represented as a UInt32, and so wrap at 4 GiB [1].

Due to its legacy nature, there’s little point filing an enhancement request against this API.

[1] The <net/if_var.h> header defines an if_data64 structure, but there’s no supported way to get that value on Apple platforms.

Limitations

When it comes to network interface statistics, certain tasks have no supported solutions:

  • Getting per-app statistics

  • Getting whole device statistics that persist across a restart

  • Getting real-time statistics for your app that persist across a restart

If you need one of these features, feel free to file an enhancement request for it. In your ER:

  • Be specific about the platforms you need this on [1].

  • Make sure that your request is aligned with that platforms privacy constraints. For example, iOS isolates your app from other apps, so you’re unlikely to get an API that returns per-app statistics for all apps on the system.

  • Supply a clear justification for why this is important to your product.

[1] If it’s macOS, be clear about:

  • Whether your app is sandboxed or not.

  • Whether it’s a Mac Catalyst.

  • Or running via iOS Apps on Mac.

Up vote post of eskimo
63 views