Swift 检测以太网/wifi网络更改

Swift 检测以太网/wifi网络更改,swift,macos,Swift,Macos,我想检测网络何时从以太网变为wifi(或wifi变为以太网)。我想让一个观察员通知我这个变化 可达性还不够好——在这两种情况下,总是通过WiFi返回可达性 附注- 以前有一些关于这个话题的问题,但是没有一个有好的答案,因为这些问题已经有一年多的历史了,也许有人已经知道了怎么做你可以在launchd下运行一点bash脚本,监视你感兴趣的界面,并在它们改变时启动一些东西 假设您的有线连接是en0,您可以运行: ./netmon en0 将此脚本另存为netmon,并使用chmod+x netmon

我想检测网络何时从以太网变为wifi(或wifi变为以太网)。我想让一个观察员通知我这个变化

可达性还不够好——在这两种情况下,总是通过WiFi返回可达性

附注-
以前有一些关于这个话题的问题,但是没有一个有好的答案,因为这些问题已经有一年多的历史了,也许有人已经知道了怎么做

你可以在
launchd
下运行一点
bash
脚本,监视你感兴趣的界面,并在它们改变时启动一些东西

假设您的有线连接是
en0
,您可以运行:

./netmon en0
将此脚本另存为
netmon
,并使用
chmod+x netmon使其可执行

#!/bin/bash
interface=$1

# Get current status of interface whose name is passed, e.g. en0
status(){
   ifconfig $1 | awk '/status:/{print $2}'
}

# Monitor interface until killed, echoing changes in status
previous=$(status $interface)
while :; do
   current=$(status $interface)
   if [ $current != $previous ]; then
      echo $interface now $current
      previous=$current
   fi 
   sleep 5
done

您可以通过
SystemConfiguration
模块访问系统网络首选项,该模块可帮助您访问当前位于默认位置的系统首选项存储
/Library/preferences/SystemConfiguration/preferences.plist

从那时起,您可以通过
SCDynamicStoreNotifyValue(::)
SCDynamicStore
接收通知,或者通过
SCDynamicStoreCopyValue(::)
检索值

直接查找当前主网络服务的示例:

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
或者使用回调创建动态存储,并设置通知键,以便在每次主网络服务更改时接收通知,通知将触发:

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)

请注意,Mac可以同时具有多个活动接口,其中一些可能是以太网接口,一些可能是WiFi接口。即使您只是监视主接口,也要注意,Mac可以有多个主接口,每个协议一个(例如,IPv4的主接口可能不是IPv6的主接口)

出于演示目的,我假设您希望监视主IPv4接口。下面是您可以复制并粘贴到swift文件并直接从命令行运行的代码(例如,
swift someFile.swift
):


当它运行时,尝试切换您的主要IPv4接口,拔出网络电缆,关闭WiFi等,然后观看输出。你可以通过点击键盘上的CTRL+C来停止它。

如果你连接到两者,你会怎么办?如果你在Mac SoMy上编程,你可以考虑CordeWLAN框架,但是我想在我的应用程序中包括WiFi/Ethernet检测,所以当用户运行这个应用程序时,应用程序将收到有关界面更改的通知并执行一些操作。@Roee84我已更新了答案以包含通知示例:)谢谢,但我想在我的应用程序中包含wifi/以太网检测,因此当用户运行应用程序时,应用程序将收到有关界面更改的通知并执行一些操作。因此,启动一个运行我的脚本的新线程,读取其输出并相应地执行操作。
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()