Search code examples
iosobjective-cxcodenetwork-programmingreachability

iOS Reachability SCNetworkReachabilitySetCallback not call back when switch form wifi to anthor wifi


I want get a call when network is change of iPhone.

I Use Reachability.m of Apple guide like this:

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
}

I test change mobile(4G) to wifi1

recives call like

Status4G
StatusNone
StatusWifi1

When change form wifi1 to wifi2

StatusWifi1
StatusNone
StatusWifi2

However, sometimes, when wifi changes very fast and did not change to StatusNone, I did not get callback when i change wifi1 to wifi2 or the other way round.

What i want get is

StatusWifi1
StatusWifi2

-------------------------UPDATE----------------------------

Thanks @Hitesh Surani, now my question is sometimes on some devices, i did not received disconnect state , I try to use

[self reachabilityWithAddress: @"www.google.com"]

Replace

[self reachabilityWithAddress: (const struct sockaddr *) &zeroAddress]

Now, I can received call back when wifi is change even it did not into disconnect state, but i still didn't known why, here is the state change:

//wifi1:
1 kSCNetworkReachabilityFlagsReachable

//wifi1 -> wifi2
2 kSCNetworkReachabilityFlagsReachable | kSCNetworkReachabilityFlagsTransientConnection
3 kSCNetworkReachabilityFlagsReachable

when change wifi, there is tmp state kSCNetworkReachabilityFlagsReachable | kSCNetworkReachabilityFlagsTransientConnection, that's why i can detect wifi changes, but what does kSCNetworkReachabilityFlagsTransientConnection mean? i read the doc of Apple, still be confused.


Solution

  • Thank you for asking great qestion.

    Mainly Reachability class is use for tracking of internet connectivity changes. There is no provsion for detect wifi changed notification. So in one word Reachability class not provide this type of funtionality. If you want to achieved your requirement then below customisation required.

    You can achived wifi changed notification as below.

    • When your wifi connection changed or switch to any other network then you have received disconnect state for fraction of second. So reachabilityChanged method is called.

    • By using below code, You can get wifi information such as BSSID,SSID(Name) and SSID data. Just store wifi info into local.

    • When network changed then you can check new wifi information is matched with previos one or not. If not then user connect with new wifi.

    Put below code into reachabilityChanged method.

    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
                        }
                    }
                }
            }
    

    Update:

    Apple provide com.apple.system.config.network_change to listen wifi changes notification. Basically it is part of Core Foundation framework. I am sure it will work for you.

    Please add below code to listen wifi changes.

     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)
        }
    

    Register observer on init and remove on deinit. Find reference from Here.