Cocoa: How to get broadcast IP address in Swift? - swift

Hi I am able to get local network IP address using this solution. I tried to use this solution to get broadcast IP address by using this Objective-C solution.
static func getBroadCastAddress() -> String? {
var address: String?
var ifaddr: UnsafeMutablePointer<ifaddrs>? = nil
if getifaddrs(&ifaddr) == 0 {
var ptr = ifaddr
while ptr != nil {
defer { ptr = ptr?.pointee.ifa_next }
guard let interface = ptr?.pointee else { return "" }
let addrFamily = interface.ifa_addr.pointee.sa_family
if addrFamily == UInt8(AF_INET) || addrFamily == UInt8(AF_INET6) {
let name: String = String(cString: (interface.ifa_name))
if name == "en0" || name == "en2" || name == "en3" || name == "en4" || name == "pdp_ip0" || name == "pdp_ip1" || name == "pdp_ip2" || name == "pdp_ip3" {
var hostname = [CChar](repeating: 0, count: Int(NI_MAXHOST))
getnameinfo(interface.ifa_addr, socklen_t((interface.ifa_addr.pointee.sa_len)), &hostname, socklen_t(hostname.count), nil, socklen_t(0), NI_NUMERICHOST)
// inet_ntoa(((struct sockaddr_in *)temp_addr->ifa_dstaddr)->sin_addr)
if let sinAddress = (interface.ifa_dstaddr as? sockaddr_in)?.sin_addr{ //=> It's always fail
address = String(utf8String: inet_ntoa(sinAddress))
}
}
}
}
freeifaddrs(ifaddr)
}
return address ?? ""
}
The problem is that I can't get sin_addr from interface like inet_ntoa(((struct sockaddr_in *)temp_addr->ifa_dstaddr)->sin_addr) in objective c code.
Can anyone help me to get broadcast IP address in Cocoa using Swift? Thank you for your time.

let acceptedInterfaces = ["en0", "en2", "en3", "en4", "pdp_ip0", "pdp_ip1", "pdp_ip2", "pdp_ip3"]
func getBroadCastAddress() {
var interfaceAddresses : [String : String] = [:]
var ifaddr: UnsafeMutablePointer<ifaddrs>? = nil
if getifaddrs(&ifaddr) == 0 {
defer { freeifaddrs(ifaddr) }
var currentInterface = ifaddr
while currentInterface != nil {
defer { currentInterface = currentInterface!.pointee.ifa_next }
let interface = currentInterface!.pointee
let addrFamily = interface.ifa_addr.pointee.sa_family
guard addrFamily == UInt8(AF_INET) || addrFamily == UInt8(AF_INET6) else { continue }
let interfaceName = String(cString: interface.ifa_name)
if acceptedInterfaces.contains(interfaceName) {
currentInterface!.withMemoryRebound(to: sockaddr_in.self, capacity: 1) {
sockaddr in
if let addressCString = inet_ntoa(sockaddr.pointee.sin_addr) {
interfaceAddresses[interfaceName] = String(cString: addressCString)
}
}
}
}
}
debugPrint(interfaceAddresses)
}
it prints:
["en0": "194.127.0.0"]

Related

Get IP address of Apple Watch

How to access IP address of Apple Watch programatically? I have used UIDevice extension in iOS for finding IP. This is an extension of UIDevice but which is not supported in watchOS what could be the other option?
extension UIDevice {
private struct InterfaceNames {
static let wifi = ["en0"]
static let wired = ["en2", "en3", "en4"]
static let cellular = ["pdp_ip0","pdp_ip1","pdp_ip2","pdp_ip3"]
static let supported = wifi + wired + cellular
}
func ipAddress() -> String? {
var ipAddress: String?
var ifaddr: UnsafeMutablePointer<ifaddrs>?
if getifaddrs(&ifaddr) == 0 {
var pointer = ifaddr
while pointer != nil {
defer { pointer = pointer?.pointee.ifa_next }
guard
let interface = pointer?.pointee,
interface.ifa_addr.pointee.sa_family == UInt8(AF_INET) || interface.ifa_addr.pointee.sa_family == UInt8(AF_INET6),
let interfaceName = interface.ifa_name,
let interfaceNameFormatted = String(cString: interfaceName, encoding: .utf8),
InterfaceNames.supported.contains(interfaceNameFormatted)
else { continue }
var hostname = [CChar](repeating: 0, count: Int(NI_MAXHOST))
getnameinfo(interface.ifa_addr,
socklen_t(interface.ifa_addr.pointee.sa_len),
&hostname,
socklen_t(hostname.count),
nil,
socklen_t(0),
NI_NUMERICHOST)
guard
let formattedIpAddress = String(cString: hostname, encoding: .utf8),
!formattedIpAddress.isEmpty
else { continue }
ipAddress = formattedIpAddress
break
}
freeifaddrs(ifaddr)
}
return ipAddress
}
}
UIDevice.current.ipAddress()
How is this possible in watchOS

Swift Get IP address of current device in wifi and cellular

func getIPAddress() -> String {
var address: String?
var ifaddr: UnsafeMutablePointer<ifaddrs>? = nil
if getifaddrs(&ifaddr) == 0 {
var ptr = ifaddr
while ptr != nil {
defer { ptr = ptr?.pointee.ifa_next }
let interface = ptr?.pointee
let addrFamily = interface?.ifa_addr.pointee.sa_family
if addrFamily == UInt8(AF_INET) || addrFamily == UInt8(AF_INET6) {
// wifi = ["en0"]
// wired = ["en2", "en3", "en4"]
// cellular = ["pdp_ip0","pdp_ip1","pdp_ip2","pdp_ip3"]
let name: String = String(cString: (interface!.ifa_name))
if name == "en0" || name == "en2" || name == "en3" || name == "en4" || name == "pdp_ip0" || name == "pdp_ip1" || name == "pdp_ip2" || name == "pdp_ip3" {
var hostname = [CChar](repeating: 0, count: Int(NI_MAXHOST))
getnameinfo(interface?.ifa_addr, socklen_t((interface?.ifa_addr.pointee.sa_len)!), &hostname, socklen_t(hostname.count), nil, socklen_t(0), NI_NUMERICHOST)
address = String(cString: hostname)
}
}
}
freeifaddrs(ifaddr)
}
return address ?? ""
}
I have use this one and run the program in iOS simulator its giving nil value. I have added the Bridging-Header file and #include in it.
for retrieve the value I have used
let stringIPAddress : String = self.getIPAddress()
can I know is that possible to get IP address with simulator
Code works for Swift 5+ and iOS 13+
To get IPAddress of device on turning Wifi or Wired or Mobile data ON, use below method :
func getIPAddress() -> String? {
var address : String?
// Get list of all interfaces on the local machine:
var ifaddr : UnsafeMutablePointer<ifaddrs>?
guard getifaddrs(&ifaddr) == 0 else { return nil }
guard let firstAddr = ifaddr else { return nil }
// For each interface ...
for ifptr in sequence(first: firstAddr, next: { $0.pointee.ifa_next }) {
let interface = ifptr.pointee
// Check for IPv4 or IPv6 interface:
let addrFamily = interface.ifa_addr.pointee.sa_family
if addrFamily == UInt8(AF_INET) || addrFamily == UInt8(AF_INET6) {
// Check interface name:
// wifi = ["en0"]
// wired = ["en2", "en3", "en4"]
// cellular = ["pdp_ip0","pdp_ip1","pdp_ip2","pdp_ip3"]
let name = String(cString: interface.ifa_name)
if name == "en0" || name == "en2" || name == "en3" || name == "en4" || name == "pdp_ip0" || name == "pdp_ip1" || name == "pdp_ip2" || name == "pdp_ip3" {
// Convert interface address to a human readable string:
var hostname = [CChar](repeating: 0, count: Int(NI_MAXHOST))
getnameinfo(interface.ifa_addr, socklen_t(interface.ifa_addr.pointee.sa_len),
&hostname, socklen_t(hostname.count),
nil, socklen_t(0), NI_NUMERICHOST)
address = String(cString: hostname)
}
}
}
freeifaddrs(ifaddr)
return address
}
Here, code/checks works for Wifi, Wired and Mobile data is:
if name == "en0" || name == "en2" || name == "en3" || name == "en4" || name == "pdp_ip0" || name == "pdp_ip1" || name == "pdp_ip2" || name == "pdp_ip3"
Hope will be helping! :)
I've been using this code, which has worked in simulator as well as playgrounds:
public class EnumerateNetworkInterfaces {
public struct NetworkInterfaceInfo {
let name: String
let ip: String
let netmask: String
}
public static func enumerate() -> [NetworkInterfaceInfo] {
var interfaces = [NetworkInterfaceInfo]()
// Get list of all interfaces on the local machine:
var ifaddr : UnsafeMutablePointer<ifaddrs>? = nil
if getifaddrs(&ifaddr) == 0 {
// For each interface ...
var ptr = ifaddr
while( ptr != nil) {
let flags = Int32(ptr!.pointee.ifa_flags)
var addr = ptr!.pointee.ifa_addr.pointee
// Check for running IPv4, IPv6 interfaces. Skip the loopback interface.
if (flags & (IFF_UP|IFF_RUNNING|IFF_LOOPBACK)) == (IFF_UP|IFF_RUNNING) {
if addr.sa_family == UInt8(AF_INET) || addr.sa_family == UInt8(AF_INET6) {
var mask = ptr!.pointee.ifa_netmask.pointee
// Convert interface address to a human readable string:
let zero = CChar(0)
var hostname = [CChar](repeating: zero, count: Int(NI_MAXHOST))
var netmask = [CChar](repeating: zero, count: Int(NI_MAXHOST))
if (getnameinfo(&addr, socklen_t(addr.sa_len), &hostname, socklen_t(hostname.count),
nil, socklen_t(0), NI_NUMERICHOST) == 0) {
let address = String(cString: hostname)
let name = ptr!.pointee.ifa_name!
let ifname = String(cString: name)
if (getnameinfo(&mask, socklen_t(mask.sa_len), &netmask, socklen_t(netmask.count),
nil, socklen_t(0), NI_NUMERICHOST) == 0) {
let netmaskIP = String(cString: netmask)
let info = NetworkInterfaceInfo(name: ifname,
ip: address,
netmask: netmaskIP)
interfaces.append(info)
}
}
}
}
ptr = ptr!.pointee.ifa_next
}
freeifaddrs(ifaddr)
}
return interfaces
}
}
And then you can do something like this:
for interface in EnumerateNetworkInterfaces.enumerate() {
print("\(interface.name): \(interface.ip)")
}

How to detect if the internet connection is over WiFi or Ethernet?

Is there a way to open network settings programmatically? Closest thing I know is opening the main settings page:
let settingsURL = NSURL(string: UIApplicationOpenSettingsURLString)!
UIApplication.sharedApplication().openURL(settingsURL)
I want to be able to detect if the internet connection is over WiFi or Ethernet.
The way to detect this is to look at the name of the network interfaces. For Mac and Apple TV, en0 and en1 refer to the wired and wireless interfaces respectively.
Add this to your bridging header (or create one if needed):
#include <ifaddrs.h>
#include <net/if_dl.h>
Then use this Swift code to get the information you need:
struct Networking {
enum NetworkInterfaceType: String, CustomStringConvertible {
case Ethernet = "en0"
case Wifi = "en1"
case Unknown = ""
var description: String {
switch self {
case .Ethernet:
return "Ethernet"
case .Wifi:
return "Wifi"
case .Unknown:
return "Unknown"
}
}
}
static var networkInterfaceType: NetworkInterfaceType {
if let name = Networking().getInterfaces().first?.name, let type = NetworkInterfaceType(rawValue: name) {
return type
}
return .Unknown
}
static var isConnectedByEthernet: Bool {
let networking = Networking()
for addr in networking.getInterfaces() {
if addr.name == NetworkInterfaceType.Ethernet.rawValue {
return true
}
}
return false
}
static var isConnectedByWiFi: Bool {
let networking = Networking()
for addr in networking.getInterfaces() {
if addr.name == NetworkInterfaceType.Wifi.rawValue {
return true
}
}
return false
}
// Credit to Martin R http://stackoverflow.com/a/34016247/600753 for this lovely code
// New Swift 3 implementation needed upated to replace unsafepointer calls with .withMemoryRebound
func getInterfaces() -> [(name : String, addr: String, mac : String)] {
var addresses = [(name : String, addr: String, mac : String)]()
var nameToMac = [ String: String ]()
// Get list of all interfaces on the local machine:
var ifaddr : UnsafeMutablePointer<ifaddrs>?
guard getifaddrs(&ifaddr) == 0 else { return [] }
guard let firstAddr = ifaddr else { return [] }
// For each interface ...
for ptr in sequence(first: firstAddr, next: { $0.pointee.ifa_next }) {
let flags = Int32(ptr.pointee.ifa_flags)
if var addr = ptr.pointee.ifa_addr {
let name = String(cString: ptr.pointee.ifa_name)
// Check for running IPv4, IPv6 interfaces. Skip the loopback interface.
if (flags & (IFF_UP|IFF_RUNNING|IFF_LOOPBACK)) == (IFF_UP|IFF_RUNNING) {
switch Int32(addr.pointee.sa_family) {
case AF_LINK:
nameToMac[name] = withUnsafePointer(to: &addr) { unsafeAddr in
unsafeAddr.withMemoryRebound(to: sockaddr_dl.self, capacity: 1) { dl in
dl.withMemoryRebound(to: Int8.self, capacity: 1) { dll in
let lladdr = UnsafeRawBufferPointer(start: dll + 8 + Int(dl.pointee.sdl_nlen), count: Int(dl.pointee.sdl_alen))
if lladdr.count == 6 {
return lladdr.map { String(format:"%02hhx", $0)}.joined(separator: ":")
} else {
return nil
}
}
}
}
case AF_INET, AF_INET6:
// Convert interface address to a human readable string:
var hostname = [CChar](repeating: 0, count: Int(NI_MAXHOST))
if (getnameinfo(addr, socklen_t(addr.pointee.sa_len),
&hostname, socklen_t(hostname.count),
nil, socklen_t(0), NI_NUMERICHOST) == 0) {
let address = String(cString: hostname)
addresses.append( (name: name, addr: address, mac : "") )
}
default:
break
}
}
}
}
freeifaddrs(ifaddr)
// Now add the mac address to the tuples:
for (i, addr) in addresses.enumerated() {
if let mac = nameToMac[addr.name] {
addresses[i] = (name: addr.name, addr: addr.addr, mac : mac)
}
}
return addresses
}
}
Usage is:
debugPrint(Networking.networkInterfaceType)
Or:
switch Networking.networkInterfaceType {
case .Ethernet:
// do something
break
case .Wifi:
// do something else
break
default:
break
}
For iOS 12.0+, tvOS 12.0+, macOS 10.14+ and watchOS 5.0+ apps you can use NWPathMonitor to solve the problem that you described in your question. Add this code to your application(_:didFinishLaunchingWithOptions:) implementation (Swift 5.1.3/Xcode 11.3.1):
let pathMonitor = NWPathMonitor()
pathMonitor.pathUpdateHandler = { path in
if path.status == .satisfied {
if path.usesInterfaceType(.wifi) {
print("wifi")
} else if path.usesInterfaceType(.cellular) {
print("cellular")
} else if path.usesInterfaceType(.wiredEthernet) {
print("wiredEthernet")
} else if path.usesInterfaceType(.loopback) {
print("loopback")
} else if path.usesInterfaceType(.other) {
print("other")
}
} else {
print("not connected")
}
}
pathMonitor.start(queue: .global(qos: .background))
And don't forget to add import Network to the top of the file.
You can use Reachability API.
let reachability: Reachability = Reachability.reachabilityForInternetConnection()
(reachability.currentReachabilityStatus().value == ReachableViaWiFi.value) // For WiFi
(reachability.currentReachabilityStatus().value == ReachableViaWWAN.value) // For WWAN
(reachability.currentReachabilityStatus().value == NotReachable.value) // For No Internet

How can I get a real IP address from DNS query in Swift?

I want to get the IP address (like 192.168.0.1 or 87.12.56.50) from DNS query in Swift. I tried 100 times with 100 different methods ... Nothing helped me, so I'll have to ask for help.
This is my code so far:
let host = CFHostCreateWithName(nil,"subdomain.of.stackoverflow.com").takeUnretainedValue();
CFHostStartInfoResolution(host, .Addresses, nil);
var success: Boolean = 0;
let addresses = CFHostGetAddressing(host, &success).takeUnretainedValue() as NSArray;
if(addresses.count > 0){
let theAddress = addresses[0] as NSData;
println(theAddress);
}
OK ... These are the links for the code I tried to implement without success:
https://gist.github.com/mikeash/bca3a341db74221625f5
How to perform DNS query on iOS
Create an Array in Swift from an NSData Object
Does CFHostGetAddressing() support ipv6 DNS entries?
Do a simple DNS lookup in Swift
Your code retrieves the address as a "socket address" structure.
getnameinfo() can be used to convert the address into a numerical IP string
(code recycled from https://stackoverflow.com/a/25627545/1187415,
now updated to Swift 2):
let host = CFHostCreateWithName(nil,"www.google.com").takeRetainedValue()
CFHostStartInfoResolution(host, .Addresses, nil)
var success: DarwinBoolean = false
if let addresses = CFHostGetAddressing(host, &success)?.takeUnretainedValue() as NSArray?,
let theAddress = addresses.firstObject as? NSData {
var hostname = [CChar](count: Int(NI_MAXHOST), repeatedValue: 0)
if getnameinfo(UnsafePointer(theAddress.bytes), socklen_t(theAddress.length),
&hostname, socklen_t(hostname.count), nil, 0, NI_NUMERICHOST) == 0 {
if let numAddress = String.fromCString(hostname) {
print(numAddress)
}
}
}
Output (example): 173.194.112.147
Note also the usage of takeRetainedValue() in the first line, because
CFHostCreateWithName() has "Create" in its name the therefore returns a (+1) retained
object.
Update for Swift 3/Xcode 8:
let host = CFHostCreateWithName(nil,"www.google.com" as CFString).takeRetainedValue()
CFHostStartInfoResolution(host, .addresses, nil)
var success: DarwinBoolean = false
if let addresses = CFHostGetAddressing(host, &success)?.takeUnretainedValue() as NSArray?,
let theAddress = addresses.firstObject as? NSData {
var hostname = [CChar](repeating: 0, count: Int(NI_MAXHOST))
if getnameinfo(theAddress.bytes.assumingMemoryBound(to: sockaddr.self), socklen_t(theAddress.length),
&hostname, socklen_t(hostname.count), nil, 0, NI_NUMERICHOST) == 0 {
let numAddress = String(cString: hostname)
print(numAddress)
}
}
Or, to get all IP addresses for the host:
let host = CFHostCreateWithName(nil,"www.google.com" as CFString).takeRetainedValue()
CFHostStartInfoResolution(host, .addresses, nil)
var success: DarwinBoolean = false
if let addresses = CFHostGetAddressing(host, &success)?.takeUnretainedValue() as NSArray? {
for case let theAddress as NSData in addresses {
var hostname = [CChar](repeating: 0, count: Int(NI_MAXHOST))
if getnameinfo(theAddress.bytes.assumingMemoryBound(to: sockaddr.self), socklen_t(theAddress.length),
&hostname, socklen_t(hostname.count), nil, 0, NI_NUMERICHOST) == 0 {
let numAddress = String(cString: hostname)
print(numAddress)
}
}
}
Refer to the following Swift code to get DNS resolution for a website. One method uses CFHostStartInfoResolution whereas other one uses gethostbyname.
Both these APIs support all/multiple IP address resolution.
private func urlToIP_cfHostResolution(_ url: String) -> [String] {
var ipList: [String] = []
let host = CFHostCreateWithName(nil,url as CFString).takeRetainedValue()
CFHostStartInfoResolution(host, .addresses, nil)
var success: DarwinBoolean = false
if let addresses = CFHostGetAddressing(host, &success)?.takeUnretainedValue() as NSArray? {
for case let theAddress as NSData in addresses {
var hostname = [CChar](repeating: 0, count: Int(NI_MAXHOST))
if getnameinfo(theAddress.bytes.assumingMemoryBound(to: sockaddr.self), socklen_t(theAddress.length),
&hostname, socklen_t(hostname.count), nil, 0, NI_NUMERICHOST) == 0 {
ipList.append(String(cString: hostname))
}
}
}
return ipList
}
This method returns ["151.101.129.69", "151.101.1.69", "151.101.193.69", "151.101.65.69"] for www.stackoverflow.com
private func urlToIP_gethostbyname(_ url: URL) -> [String] {
var ipList: [String] = []
guard let hostname = url.host else {
return ipList
}
guard let host = hostname.withCString({gethostbyname($0)}) else {
return ipList
}
guard host.pointee.h_length > 0 else {
return ipList
}
var index = 0
while host.pointee.h_addr_list[index] != nil {
var addr: in_addr = in_addr()
memcpy(&addr.s_addr, host.pointee.h_addr_list[index], Int(host.pointee.h_length))
guard let remoteIPAsC = inet_ntoa(addr) else {
return ipList
}
ipList.append(String.init(cString: remoteIPAsC))
index += 1
}
return ipList
}
This method also returns ["151.101.129.69", "151.101.1.69", "151.101.193.69", "151.101.65.69"] for www.stackoverflow.com
Hope this helps.
A number of the answers are using the deprecated bytes to retrieve the sockaddr to supply to getnameinfo. We’d use address.withUnsafeBytes now. For example, in Swift 5:
/// Returns the string representation of the supplied address.
///
/// - parameter address: Contains a `(struct sockaddr)` with the address to render.
///
/// - returns: A string representation of that address.
func stringRepresentation(forAddress address: Data) -> String? {
address.withUnsafeBytes { pointer in
var hostStr = [Int8](repeating: 0, count: Int(NI_MAXHOST))
let result = getnameinfo(
pointer.baseAddress?.assumingMemoryBound(to: sockaddr.self),
socklen_t(address.count),
&hostStr,
socklen_t(hostStr.count),
nil,
0,
NI_NUMERICHOST
)
guard result == 0 else { return nil }
return String(cString: hostStr)
}
}

iPhone Data Usage Tracking/Monitoring

I've searched over this topic but found very few details which were helpful. With these details I've tried to cook some code as follows.
Note: Please compare the details shared in this post with other posts before marking this as DUPLICATE, and not just by the subject.
- (NSArray *)getDataCountersForType:(int)type {
BOOL success;
struct ifaddrs *addrs = nil;
const struct ifaddrs *cursor = nil;
const struct sockaddr_dl *dlAddr = nil;
const struct if_data *networkStatisc = nil;
int dataSent = 0;
int dataReceived = 0;
success = getifaddrs(&addrs) == 0;
if (success) {
cursor = addrs;
while (cursor != NULL) {
if (cursor->ifa_addr->sa_family == AF_LINK) {
dlAddr = (const struct sockaddr_dl *) cursor->ifa_addr;
networkStatisc = (const struct if_data *) cursor->ifa_data;
if (type == WiFi) {
dataSent += networkStatisc->ifi_opackets;
dataReceived += networkStatisc->ifi_ipackets;
}
else if (type == WWAN) {
dataSent += networkStatisc->ifi_obytes;
dataReceived += networkStatisc->ifi_ibytes;
}
}
cursor = cursor->ifa_next;
}
freeifaddrs(addrs);
}
return [NSArray arrayWithObjects:[NSNumber numberWithInt:dataSent], [NSNumber numberWithInt:dataReceived], nil];
}
This code collects information of internet usage of an iPhone device (and not my application alone).
Now, if I use internet through WiFi or through 3G, I get the the data (bytes) only in ifi_obytes (sent) and ifi_ibytes (received) but I think I should get WiFi usage in ifi_opackets and ifi_ipackets.
Also wanted to add that if I'm connected to a WiFi network, but am not using internet, I still get value added to ifi_obytes and ifi_ibytes.
May be I'm wrong in the implementation or understanding. Need someone to help me out.
Edit: Instead of AF_LINK I tried AF_INET (sockaddr_in instead of sockaddr_dl). This crashes the application.
The thing is that pdp_ip0 is one of interfaces, all pdpXXX are WWAN interfaces dedicated to different functions, voicemail, general networking interface.
I read in Apple forum that :
The OS does not keep network statistics on a process-by-process basis. As such, there's no exact solution to this problem. You can, however, get network statistics for each network interface.
In general en0 is your Wi-Fi interface and pdp_ip0 is your WWAN interface.
There is no good way to get information wifi/cellular network data since, particular date-time!
Data statistic (ifa_data->ifi_obytes and ifa_data->ifi_ibytes) are stored from previous device reboot.
I don't know why, but ifi_opackets and ifi_ipackets are shown just for lo0 (I think its main interface ).
Yes. Then device is connected via WiFi and doesn't use internet if_iobytes values still come because this method provides network bytes exchanges and not just internet.
#include <net/if.h>
#include <ifaddrs.h>
static NSString *const DataCounterKeyWWANSent = #"WWANSent";
static NSString *const DataCounterKeyWWANReceived = #"WWANReceived";
static NSString *const DataCounterKeyWiFiSent = #"WiFiSent";
static NSString *const DataCounterKeyWiFiReceived = #"WiFiReceived";
NSDictionary *DataCounters()
{
struct ifaddrs *addrs;
const struct ifaddrs *cursor;
u_int32_t WiFiSent = 0;
u_int32_t WiFiReceived = 0;
u_int32_t WWANSent = 0;
u_int32_t WWANReceived = 0;
if (getifaddrs(&addrs) == 0)
{
cursor = addrs;
while (cursor != NULL)
{
if (cursor->ifa_addr->sa_family == AF_LINK)
{
#ifdef DEBUG
const struct if_data *ifa_data = (struct if_data *)cursor->ifa_data;
if (ifa_data != NULL)
{
NSLog(#"Interface name %s: sent %tu received %tu",cursor->ifa_name,ifa_data->ifi_obytes,ifa_data->ifi_ibytes);
}
#endif
// name of interfaces:
// en0 is WiFi
// pdp_ip0 is WWAN
NSString *name = #(cursor->ifa_name);
if ([name hasPrefix:#"en"])
{
const struct if_data *ifa_data = (struct if_data *)cursor->ifa_data;
if (ifa_data != NULL)
{
WiFiSent += ifa_data->ifi_obytes;
WiFiReceived += ifa_data->ifi_ibytes;
}
}
if ([name hasPrefix:#"pdp_ip"])
{
const struct if_data *ifa_data = (struct if_data *)cursor->ifa_data;
if (ifa_data != NULL)
{
WWANSent += ifa_data->ifi_obytes;
WWANReceived += ifa_data->ifi_ibytes;
}
}
}
cursor = cursor->ifa_next;
}
freeifaddrs(addrs);
}
return #{DataCounterKeyWiFiSent : #(WiFiSent),
DataCounterKeyWiFiReceived : #(WiFiReceived),
DataCounterKeyWWANSent : #(WWANSent),
DataCounterKeyWWANReceived : #(WWANReceived)};
}
Improved copy/paste support !
It's important to understand that these counters are provided since the device's last boot.
So, to make effective use of them, you should accompany every sample with the device's uptime (you can use mach_absolute_time() - see this for more information)
Once you have counters samples + uptime you can have better heuristics as to data use...
To add to the accepted answer, its important to realize that the amount of data displayed by the interface overflows and restarts at 0 after every 4 GB, especially if you are using this code to calculate the difference between two readings. This is because ifi_obytes and ifi_ibytes are uint_32 and their max value is 4294967295.
Also, I recommend using unsigned ints for the variables containing the data sent and received. Regular ints have half the max value of an unsigned integer, so when adding ifi_obytes, it may cause an overflow.
unsigned int sent = 0;
sent += networkStatisc->ifi_obytes;
Swift version of the accepted answer. I also break the code into smaller units.
struct DataUsageInfo {
var wifiReceived: UInt32 = 0
var wifiSent: UInt32 = 0
var wirelessWanDataReceived: UInt32 = 0
var wirelessWanDataSent: UInt32 = 0
mutating func updateInfoByAdding(info: DataUsageInfo) {
wifiSent += info.wifiSent
wifiReceived += info.wifiReceived
wirelessWanDataSent += info.wirelessWanDataSent
wirelessWanDataReceived += info.wirelessWanDataReceived
}
}
class DataUsage {
private static let wwanInterfacePrefix = "pdp_ip"
private static let wifiInterfacePrefix = "en"
class func getDataUsage() -> DataUsageInfo {
var interfaceAddresses: UnsafeMutablePointer<ifaddrs> = nil
var dataUsageInfo = DataUsageInfo()
guard getifaddrs(&interfaceAddresses) == 0 else { return dataUsageInfo }
var pointer = interfaceAddresses
while pointer != nil {
guard let info = getDataUsageInfo(from: pointer) else {
pointer = pointer.memory.ifa_next
continue
}
dataUsageInfo.updateInfoByAdding(info)
pointer = pointer.memory.ifa_next
}
freeifaddrs(interfaceAddresses)
return dataUsageInfo
}
private class func getDataUsageInfo(from infoPointer: UnsafeMutablePointer<ifaddrs>) -> DataUsageInfo? {
let pointer = infoPointer
let name: String! = String.fromCString(infoPointer.memory.ifa_name)
let addr = pointer.memory.ifa_addr.memory
guard addr.sa_family == UInt8(AF_LINK) else { return nil }
return dataUsageInfo(from: pointer, name: name)
}
private class func dataUsageInfo(from pointer: UnsafeMutablePointer<ifaddrs>, name: String) -> DataUsageInfo {
var networkData: UnsafeMutablePointer<if_data> = nil
var dataUsageInfo = DataUsageInfo()
if name.hasPrefix(wifiInterfacePrefix) {
networkData = unsafeBitCast(pointer.memory.ifa_data, UnsafeMutablePointer<if_data>.self)
dataUsageInfo.wifiSent += networkData.memory.ifi_obytes
dataUsageInfo.wifiReceived += networkData.memory.ifi_ibytes
} else if name.hasPrefix(wwanInterfacePrefix) {
networkData = unsafeBitCast(pointer.memory.ifa_data, UnsafeMutablePointer<if_data>.self)
dataUsageInfo.wirelessWanDataSent += networkData.memory.ifi_obytes
dataUsageInfo.wirelessWanDataReceived += networkData.memory.ifi_ibytes
}
return dataUsageInfo
}
}
I fixed above source code to Swift3 version
struct DataUsageInfo {
var wifiReceived: UInt32 = 0
var wifiSent: UInt32 = 0
var wirelessWanDataReceived: UInt32 = 0
var wirelessWanDataSent: UInt32 = 0
mutating func updateInfoByAdding(_ info: DataUsageInfo) {
wifiSent += info.wifiSent
wifiReceived += info.wifiReceived
wirelessWanDataSent += info.wirelessWanDataSent
wirelessWanDataReceived += info.wirelessWanDataReceived
}
}
class DataUsage {
private static let wwanInterfacePrefix = "pdp_ip"
private static let wifiInterfacePrefix = "en"
class func getDataUsage() -> DataUsageInfo {
var ifaddr: UnsafeMutablePointer<ifaddrs>?
var dataUsageInfo = DataUsageInfo()
guard getifaddrs(&ifaddr) == 0 else { return dataUsageInfo }
while let addr = ifaddr {
guard let info = getDataUsageInfo(from: addr) else {
ifaddr = addr.pointee.ifa_next
continue
}
dataUsageInfo.updateInfoByAdding(info)
ifaddr = addr.pointee.ifa_next
}
freeifaddrs(ifaddr)
return dataUsageInfo
}
private class func getDataUsageInfo(from infoPointer: UnsafeMutablePointer<ifaddrs>) -> DataUsageInfo? {
let pointer = infoPointer
let name: String! = String(cString: pointer.pointee.ifa_name)
let addr = pointer.pointee.ifa_addr.pointee
guard addr.sa_family == UInt8(AF_LINK) else { return nil }
return dataUsageInfo(from: pointer, name: name)
}
private class func dataUsageInfo(from pointer: UnsafeMutablePointer<ifaddrs>, name: String) -> DataUsageInfo {
var networkData: UnsafeMutablePointer<if_data>?
var dataUsageInfo = DataUsageInfo()
if name.hasPrefix(wifiInterfacePrefix) {
networkData = unsafeBitCast(pointer.pointee.ifa_data, to: UnsafeMutablePointer<if_data>.self)
if let data = networkData {
dataUsageInfo.wifiSent += data.pointee.ifi_obytes
dataUsageInfo.wifiReceived += data.pointee.ifi_ibytes
}
} else if name.hasPrefix(wwanInterfacePrefix) {
networkData = unsafeBitCast(pointer.pointee.ifa_data, to: UnsafeMutablePointer<if_data>.self)
if let data = networkData {
dataUsageInfo.wirelessWanDataSent += data.pointee.ifi_obytes
dataUsageInfo.wirelessWanDataReceived += data.pointee.ifi_ibytes
}
}
return dataUsageInfo
}
}
A new version about based on previous versions, but adapted for Swift4 and Xcode 9
struct DataUsageInfo {
var wifiReceived: UInt32 = 0
var wifiSent: UInt32 = 0
var wirelessWanDataReceived: UInt32 = 0
var wirelessWanDataSent: UInt32 = 0
mutating func updateInfoByAdding(info: DataUsageInfo) {
wifiSent += info.wifiSent
wifiReceived += info.wifiReceived
wirelessWanDataSent += info.wirelessWanDataSent
wirelessWanDataReceived += info.wirelessWanDataReceived
}
}
class DataUsage {
private static let wwanInterfacePrefix = "pdp_ip"
private static let wifiInterfacePrefix = "en"
class func getDataUsage() -> DataUsageInfo {
var interfaceAddresses: UnsafeMutablePointer<ifaddrs>? = nil
var dataUsageInfo = DataUsageInfo()
guard getifaddrs(&interfaceAddresses) == 0 else { return dataUsageInfo }
var pointer = interfaceAddresses
while pointer != nil {
guard let info = getDataUsageInfo(from: pointer!) else {
pointer = pointer!.pointee.ifa_next
continue
}
dataUsageInfo.updateInfoByAdding(info: info)
pointer = pointer!.pointee.ifa_next
}
freeifaddrs(interfaceAddresses)
return dataUsageInfo
}
private class func getDataUsageInfo(from infoPointer: UnsafeMutablePointer<ifaddrs>) -> DataUsageInfo? {
let pointer = infoPointer
let name: String! = String(cString: infoPointer.pointee.ifa_name)
let addr = pointer.pointee.ifa_addr.pointee
guard addr.sa_family == UInt8(AF_LINK) else { return nil }
return dataUsageInfo(from: pointer, name: name)
}
private class func dataUsageInfo(from pointer: UnsafeMutablePointer<ifaddrs>, name: String) -> DataUsageInfo {
var networkData: UnsafeMutablePointer<if_data>? = nil
var dataUsageInfo = DataUsageInfo()
if name.hasPrefix(wifiInterfacePrefix) {
networkData = unsafeBitCast(pointer.pointee.ifa_data, to: UnsafeMutablePointer<if_data>.self)
dataUsageInfo.wifiSent += networkData?.pointee.ifi_obytes ?? 0
dataUsageInfo.wifiReceived += networkData?.pointee.ifi_ibytes ?? 0
} else if name.hasPrefix(wwanInterfacePrefix) {
networkData = unsafeBitCast(pointer.pointee.ifa_data, to: UnsafeMutablePointer<if_data>.self)
dataUsageInfo.wirelessWanDataSent += networkData?.pointee.ifi_obytes ?? 0
dataUsageInfo.wirelessWanDataReceived += networkData?.pointee.ifi_ibytes ?? 0
}
return dataUsageInfo
}
}
Sorry for same answer again.
but I found that UInt32 is not enough, so it crashes when it became too big.
I just changed UInt32 to UInt64 and it works fine.
struct DataUsageInfo {
var wifiReceived: UInt64 = 0
var wifiSent: UInt64 = 0
var wirelessWanDataReceived: UInt64 = 0
var wirelessWanDataSent: UInt64 = 0
mutating func updateInfoByAdding(info: DataUsageInfo) {
wifiSent += info.wifiSent
wifiReceived += info.wifiReceived
wirelessWanDataSent += info.wirelessWanDataSent
wirelessWanDataReceived += info.wirelessWanDataReceived
}
}
class DataUsage {
private static let wwanInterfacePrefix = "pdp_ip"
private static let wifiInterfacePrefix = "en"
class func getDataUsage() -> DataUsageInfo {
var interfaceAddresses: UnsafeMutablePointer<ifaddrs>? = nil
var dataUsageInfo = DataUsageInfo()
guard getifaddrs(&interfaceAddresses) == 0 else { return dataUsageInfo }
var pointer = interfaceAddresses
while pointer != nil {
guard let info = getDataUsageInfo(from: pointer!) else {
pointer = pointer!.pointee.ifa_next
continue
}
dataUsageInfo.updateInfoByAdding(info: info)
pointer = pointer!.pointee.ifa_next
}
freeifaddrs(interfaceAddresses)
return dataUsageInfo
}
private class func getDataUsageInfo(from infoPointer: UnsafeMutablePointer<ifaddrs>) -> DataUsageInfo? {
let pointer = infoPointer
let name: String! = String(cString: infoPointer.pointee.ifa_name)
let addr = pointer.pointee.ifa_addr.pointee
guard addr.sa_family == UInt8(AF_LINK) else { return nil }
return dataUsageInfo(from: pointer, name: name)
}
private class func dataUsageInfo(from pointer: UnsafeMutablePointer<ifaddrs>, name: String) -> DataUsageInfo {
var networkData: UnsafeMutablePointer<if_data>? = nil
var dataUsageInfo = DataUsageInfo()
if name.hasPrefix(wifiInterfacePrefix) {
networkData = unsafeBitCast(pointer.pointee.ifa_data, to: UnsafeMutablePointer<if_data>.self)
dataUsageInfo.wifiSent += UInt64(networkData?.pointee.ifi_obytes ?? 0)
dataUsageInfo.wifiReceived += UInt64(networkData?.pointee.ifi_ibytes ?? 0)
} else if name.hasPrefix(wwanInterfacePrefix) {
networkData = unsafeBitCast(pointer.pointee.ifa_data, to: UnsafeMutablePointer<if_data>.self)
dataUsageInfo.wirelessWanDataSent += UInt64(networkData?.pointee.ifi_obytes ?? 0)
dataUsageInfo.wirelessWanDataReceived += UInt64(networkData?.pointee.ifi_ibytes ?? 0)
}
return dataUsageInfo
}
}