Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Detect ethernet/wifi network change

Tags:

macos

swift

I want to detect when the network changes from ethernet to wifi (or wifi to ethernet). I want to have an observer to notify me about this change.

reachability isn't good enough - it's always returns ReachableViaWiFi for both cases.

P.S - There were some questions regarding this topic before, but none of them has a good answer, and since those questions are more than a year old, maybe someone already find out how to do it

like image 774
Roee84 Avatar asked Jan 11 '17 12:01

Roee84


People also ask

How do I change what WiFi my Ethernet is connected to?

In Windows 10, click Start > Settings > Control Panel > Network and Internet > Network and Sharing Center > Change adapter settings. In the list of network connections that opens, select the connection you are using to connect to your ISP (wireless or LAN).

How can I tell if Im connected to WiFi or Ethernet?

Click the Start button, then click "Control Panel" and type "network status" in the search field at the top right of the window. Click "Network and Sharing" to see a readout of your current network status.

Why is my Ethernet cable not being detected?

Check the Ethernet cable and connection. Make sure the Ethernet cable is securely plugged into the Ethernet port on both your router and Windows 10 PC. On your router, make sure the Ethernet cable is plugged into the correct Ethernet port—not the Internet port that's used to connect your modem and router.


2 Answers

Please note that a Mac can have multiple active interfaces at the same time and some of these may be Ethernet and some of them may be WiFi. Even if you just monitor the primary interfaces, take note that a Mac can have multiple primary interfaces, one per protocol (e.g. the primary interface for IPv4 may not be the primary one for IPv6).

For demonstration purposes, I will assume that you want to monitor the primary IPv4 interface. Here is code that you can just copy & paste to a swift file and directly run from command line (e.g. swift someFile.swift):

import Foundation
import SystemConfiguration

let DynamicStore = SCDynamicStoreCreate(
    nil, "Name of your App" as CFString,
    { ( _, _, _ ) in PrimaryIPv4InterfaceChanged() }, nil)!

func PrimaryIPv4InterfaceChanged ( ) {
    guard let ipv4State = SCDynamicStoreCopyValue(DynamicStore,
        "State:/Network/Global/IPv4" as CFString) as? [CFString: Any]
        else {
            print("No primary IPv4 interface available")
            return
        }

    guard let primaryServiceID =
        ipv4State[kSCDynamicStorePropNetPrimaryService]
        else { return }

    let interfaceStateName =
        "Setup:/Network/Service/\(primaryServiceID)/Interface"
        as CFString

    guard let primaryServiceState = SCDynamicStoreCopyValue(
        DynamicStore, interfaceStateName) as? [CFString: Any]
        else { return }

    guard let hardwareType =
        primaryServiceState[kSCPropNetInterfaceHardware]
        else { return }

    switch hardwareType as! CFString {
        case kSCEntNetAirPort:
            print("Primary IPv4 interface is now WiFi")

        case kSCEntNetEthernet:
            print("Primary IPv4 interface is now Ethernet")

        default:
            print("Primary IPv4 interface is something else")
    }
}

SCDynamicStoreSetNotificationKeys(
    DynamicStore, [ "State:/Network/Global/IPv4" ] as CFArray, nil)

SCDynamicStoreSetDispatchQueue(DynamicStore, DispatchQueue.main)

dispatchMain()

While it is running, try switching your primary IPv4 interface, pull network cables, turn off WiFi, etc. and watch the output. You can stop it by hitting CTRL+C on your keyboard.

like image 195
Mecki Avatar answered Sep 30 '22 19:09

Mecki


You can access system network preferences through SystemConfiguration module, which helps you get touch to system preferences store currently resides in the default location /Library/Preferences/SystemConfiguration/preferences.plist.

Since then, you can receive notifications from SCDynamicStore by SCDynamicStoreNotifyValue(_:_:) or retrieve value by SCDynamicStoreCopyValue(_:_:).

Example for directly lookup current primary network service:

var store = SCDynamicStoreCreate(nil, "Example" as CFString, nil, nil)
var global = SCDynamicStoreCopyValue(store, "State:/Network/Global/IPv4" as CFString)!

var pref = SCPreferencesCreate(nil, "Example" as CFString, nil)
var service = SCNetworkServiceCopy(pref!, global["PrimaryService"] as! CFString)
var interface = SCNetworkServiceGetInterface(service!)

SCNetworkInterfaceGetInterfaceType(interface!) /// Optional("IEEE80211") -> Wi-Fi

Or create dynamic store with callback and set notification keys to receive notifications as every time primary network service changes the notification is going to fire:

var callback: SCDynamicStoreCallBack = { (store, _, _) in
  /* Do anything you want */
}
var store = SCDynamicStoreCreate(nil, "Example" as CFString, callback, nil)
SCDynamicStoreSetNotificationKeys(store!, ["State:/Network/Global/IPv4"] as CFArray, nil)
like image 33
legendecas Avatar answered Sep 30 '22 17:09

legendecas