iOS Reachability SCNetworkReachability SetCallback 在从 wifi 切换到另一个 wifi 时不回调
iOS Reachability SCNetworkReachabilitySetCallback not call back when switch form wifi to anthor wifi
我想在 iPhone 网络发生变化时接到电话。
我使用 Reachability.m Apple 指南是这样的:
xxx.m
struct sockaddr_in zeroAddress;
bzero(&zeroAddress, sizeof(zeroAddress));
zeroAddress.sin_len = sizeof(zeroAddress);
zeroAddress.sin_family = AF_INET;
Reachability = [self reachabilityWithAddress: (const struct sockaddr *) &zeroAddress]
Reachability.m
BOOL returnValue = NO;
SCNetworkReachabilityContext context = {0, (__bridge void *)(self), NULL, NULL, NULL};
if (SCNetworkReachabilitySetCallback(_reachabilityRef, ReachabilityCallback, &context))
{
if (SCNetworkReachabilityScheduleWithRunLoop(_reachabilityRef, CFRunLoopGetMain(), kCFRunLoopDefaultMode))
{
returnValue = YES;
}
}
return returnValue;
xxx.m
ReachabilityCallback {
//do something when network change
}
我测试把mobile(4G)换成wifi1
收到喜欢的电话
Status4G
StatusNone
StatusWifi1
当从wifi1改为wifi2时
StatusWifi1
StatusNone
StatusWifi2
但是,有时,当 wifi 变化非常快而没有变为 StatusNone
时,当我将 wifi1 更改为 wifi2 或相反时,我没有收到回调。
我想要得到的是
StatusWifi1
StatusWifi2
-------------------------UPDATE----------------------------
谢谢@Hitesh Surani,现在我的问题有时在某些设备上,我没有收到 disconnect state
,我尝试使用
[self reachabilityWithAddress: @"www.google.com"]
替换
[self reachabilityWithAddress: (const struct sockaddr *) &zeroAddress]
现在,即使 wifi 没有进入 disconnect state
,我也可以在 wifi 改变时收到回电,但我仍然不知道为什么,这是状态改变:
//wifi1:
1 kSCNetworkReachabilityFlagsReachable
//wifi1 -> wifi2
2 kSCNetworkReachabilityFlagsReachable | kSCNetworkReachabilityFlagsTransientConnection
3 kSCNetworkReachabilityFlagsReachable
当更改 wifi 时,有 tmp 状态 kSCNetworkReachabilityFlagsReachable | kSCNetworkReachabilityFlagsTransientConnection
,这就是为什么我可以检测到 wifi 更改,但是 kSCNetworkReachabilityFlagsTransientConnection
是什么意思?看了苹果的文档,还是一头雾水
感谢您提出很好的问题。
主要Reachability
class用于跟踪互联网连接变化。没有检测 wifi 更改通知的规定。所以一言以蔽之 Reachability
class 不提供这种功能。如果您想达到您的要求,则需要进行以下定制。
您可以如下所示获得 wifi 更改通知。
当您的 wifi 连接改变或切换到任何其他网络时,您会在几分之一秒内收到断开连接状态。所以reachabilityChanged
方法被调用了。
通过使用下面的代码,您可以获得诸如 BSSID、SSID(名称)和 SSID 数据等 wifi 信息。将wifi信息存储到本地即可。
- 当网络改变时,您可以检查新的 wifi 信息是否与以前的信息匹配。如果没有,则用户连接新的 wifi。
将以下代码放入 reachabilityChanged
方法中。
import SystemConfiguration.CaptiveNetwork
func printCurrentWifiInfo() {
if let interface = CNCopySupportedInterfaces() {
for i in 0..<CFArrayGetCount(interface) {
let interfaceName: UnsafeRawPointer = CFArrayGetValueAtIndex(interface, i)
let rec = unsafeBitCast(interfaceName, to: AnyObject.self)
if let unsafeInterfaceData = CNCopyCurrentNetworkInfo("\(rec)" as CFString), let interfaceData = unsafeInterfaceData as? [String : AnyObject] {
// connected wifi
print("BSSID: \(String(describing: interfaceData["BSSID"])), SSID: \(String(describing: interfaceData["SSID"])), SSIDDATA: \(String(describing: interfaceData["SSIDDATA"]))")
} else {
//Wifi is not connected
}
}
}
}
更新:
Apple 提供 com.apple.system.config.network_change
监听 wifi 变化通知。基本上它是 Core Foundation
框架的一部分。我相信它会适合你。
请添加以下代码以监听 wifi 变化。
let notificationName = "com.apple.system.config.network_change"
func onNetworkChange(_ name : String) {
if (name == notificationName) {
// Do your stuff
print("Network was changed")
}
}
func registerObserver() {
let observer = UnsafeRawPointer(Unmanaged.passUnretained(self).toOpaque())
CFNotificationCenterAddObserver(CFNotificationCenterGetDarwinNotifyCenter(), observer,
{ (nc, observer, name, _, _) -> Swift.Void in
if let observer = observer, let name = name {
let instance = Unmanaged<Reachability>.fromOpaque(observer).takeUnretainedValue()
instance.onNetworkChange(name.rawValue as String)
} },
notificationName as CFString, nil, .deliverImmediately)
}
func removeObserver() {
let observer = UnsafeRawPointer(Unmanaged.passUnretained(self).toOpaque())
CFNotificationCenterRemoveObserver(CFNotificationCenterGetDarwinNotifyCenter(), observer, nil, nil)
}
在 init 上注册观察者并在 deinit 上删除。
从 Here.
中查找参考
我想在 iPhone 网络发生变化时接到电话。
我使用 Reachability.m Apple 指南是这样的:
xxx.m
struct sockaddr_in zeroAddress;
bzero(&zeroAddress, sizeof(zeroAddress));
zeroAddress.sin_len = sizeof(zeroAddress);
zeroAddress.sin_family = AF_INET;
Reachability = [self reachabilityWithAddress: (const struct sockaddr *) &zeroAddress]
Reachability.m
BOOL returnValue = NO;
SCNetworkReachabilityContext context = {0, (__bridge void *)(self), NULL, NULL, NULL};
if (SCNetworkReachabilitySetCallback(_reachabilityRef, ReachabilityCallback, &context))
{
if (SCNetworkReachabilityScheduleWithRunLoop(_reachabilityRef, CFRunLoopGetMain(), kCFRunLoopDefaultMode))
{
returnValue = YES;
}
}
return returnValue;
xxx.m
ReachabilityCallback {
//do something when network change
}
我测试把mobile(4G)换成wifi1
收到喜欢的电话
Status4G
StatusNone
StatusWifi1
当从wifi1改为wifi2时
StatusWifi1
StatusNone
StatusWifi2
但是,有时,当 wifi 变化非常快而没有变为 StatusNone
时,当我将 wifi1 更改为 wifi2 或相反时,我没有收到回调。
我想要得到的是
StatusWifi1
StatusWifi2
-------------------------UPDATE----------------------------
谢谢@Hitesh Surani,现在我的问题有时在某些设备上,我没有收到 disconnect state
,我尝试使用
[self reachabilityWithAddress: @"www.google.com"]
替换
[self reachabilityWithAddress: (const struct sockaddr *) &zeroAddress]
现在,即使 wifi 没有进入 disconnect state
,我也可以在 wifi 改变时收到回电,但我仍然不知道为什么,这是状态改变:
//wifi1:
1 kSCNetworkReachabilityFlagsReachable
//wifi1 -> wifi2
2 kSCNetworkReachabilityFlagsReachable | kSCNetworkReachabilityFlagsTransientConnection
3 kSCNetworkReachabilityFlagsReachable
当更改 wifi 时,有 tmp 状态 kSCNetworkReachabilityFlagsReachable | kSCNetworkReachabilityFlagsTransientConnection
,这就是为什么我可以检测到 wifi 更改,但是 kSCNetworkReachabilityFlagsTransientConnection
是什么意思?看了苹果的文档,还是一头雾水
感谢您提出很好的问题。
主要Reachability
class用于跟踪互联网连接变化。没有检测 wifi 更改通知的规定。所以一言以蔽之 Reachability
class 不提供这种功能。如果您想达到您的要求,则需要进行以下定制。
您可以如下所示获得 wifi 更改通知。
当您的 wifi 连接改变或切换到任何其他网络时,您会在几分之一秒内收到断开连接状态。所以
reachabilityChanged
方法被调用了。通过使用下面的代码,您可以获得诸如 BSSID、SSID(名称)和 SSID 数据等 wifi 信息。将wifi信息存储到本地即可。
- 当网络改变时,您可以检查新的 wifi 信息是否与以前的信息匹配。如果没有,则用户连接新的 wifi。
将以下代码放入 reachabilityChanged
方法中。
import SystemConfiguration.CaptiveNetwork
func printCurrentWifiInfo() {
if let interface = CNCopySupportedInterfaces() {
for i in 0..<CFArrayGetCount(interface) {
let interfaceName: UnsafeRawPointer = CFArrayGetValueAtIndex(interface, i)
let rec = unsafeBitCast(interfaceName, to: AnyObject.self)
if let unsafeInterfaceData = CNCopyCurrentNetworkInfo("\(rec)" as CFString), let interfaceData = unsafeInterfaceData as? [String : AnyObject] {
// connected wifi
print("BSSID: \(String(describing: interfaceData["BSSID"])), SSID: \(String(describing: interfaceData["SSID"])), SSIDDATA: \(String(describing: interfaceData["SSIDDATA"]))")
} else {
//Wifi is not connected
}
}
}
}
更新:
Apple 提供 com.apple.system.config.network_change
监听 wifi 变化通知。基本上它是 Core Foundation
框架的一部分。我相信它会适合你。
请添加以下代码以监听 wifi 变化。
let notificationName = "com.apple.system.config.network_change"
func onNetworkChange(_ name : String) {
if (name == notificationName) {
// Do your stuff
print("Network was changed")
}
}
func registerObserver() {
let observer = UnsafeRawPointer(Unmanaged.passUnretained(self).toOpaque())
CFNotificationCenterAddObserver(CFNotificationCenterGetDarwinNotifyCenter(), observer,
{ (nc, observer, name, _, _) -> Swift.Void in
if let observer = observer, let name = name {
let instance = Unmanaged<Reachability>.fromOpaque(observer).takeUnretainedValue()
instance.onNetworkChange(name.rawValue as String)
} },
notificationName as CFString, nil, .deliverImmediately)
}
func removeObserver() {
let observer = UnsafeRawPointer(Unmanaged.passUnretained(self).toOpaque())
CFNotificationCenterRemoveObserver(CFNotificationCenterGetDarwinNotifyCenter(), observer, nil, nil)
}
在 init 上注册观察者并在 deinit 上删除。 从 Here.
中查找参考