8

This is for a tweak, so the target is jailbroken devices, and not the app store. I have tried hooking different methods in the SBWiFiManager but they either are called when the wifi strength changes (so continuously) or after quite delay after the network has changed.

Is there any other way to get a notification (or another method to hook) went the wifi network changes?

I know you can get the current SSID with public APIs now, but I need to be told when it changes.

Nate
  • 31,017
  • 13
  • 83
  • 207
Jonathan.
  • 53,997
  • 54
  • 186
  • 290

2 Answers2

18

One way to do this is to listen for the com.apple.system.config.network_change event from the Core Foundation Darwin notification center.

Register for the event:

CFNotificationCenterAddObserver(CFNotificationCenterGetDarwinNotifyCenter(), //center
                                NULL, // observer
                                onNotifyCallback, // callback
                                CFSTR("com.apple.system.config.network_change"), // event name
                                NULL, // object
                                CFNotificationSuspensionBehaviorDeliverImmediately);

Here's a sample callback:

static void onNotifyCallback(CFNotificationCenterRef center, void *observer, CFStringRef name, const void *object, CFDictionaryRef userInfo)
{
    NSString* notifyName = (NSString*)name;
    // this check should really only be necessary if you reuse this one callback method
    //  for multiple Darwin notification events
    if ([notifyName isEqualToString:@"com.apple.system.config.network_change"]) {
        // use the Captive Network API to get more information at this point
        //  https://stackoverflow.com/a/4714842/119114
    } else {
        NSLog(@"intercepted %@", notifyName);
    }
}

See my link to another answer on how to use the Captive Network API to get the current SSID, for example.

Note that although the phone I tested this on is jailbroken (iOS 6.1), I don't think this requires jailbreaking to work correctly. It certainly doesn't require the app being installed outside the normal sandbox area (/var/mobile/Applications/*).

P.S. I haven't tested this exhaustively enough to know whether this event gives any false positives (based on your definition of a network change). However, it's simple enough just to store some state variable, equal to the last network's SSID, and compare that to the current one, whenever this event comes in.

Community
  • 1
  • 1
Nate
  • 31,017
  • 13
  • 83
  • 207
  • Thank you!!! (Btw I just put the bit about jailbreak to stop people saying it would rejected by the AppStore). Out of curiosity how did you find the name of the notification? – Jonathan. Feb 15 '13 at 11:12
  • 3
    @Jonathan, can you ask a new question, that inquires about general techniques used to determine which notifications are used? I ask, not just because I want to squeek out a couple more rep points, but because (a) I can't fit the answer in a comment, and (b) it will be more useful (and searchable) for others if the question is appropriately titled, tagged, and described. Comments aren't good for much more than just short responses. You can certainly *link* to this question as a *specific* example of one such reason to have these techniques. – Nate Feb 15 '13 at 19:26
  • "com.apple.system.config.network_change" often notify four times. Why? Do you some idea to check or coalesce one ? Thanks. – Victor Choy Mar 02 '20 at 10:08
  • Yes, it looks like we can save some state like bssid. However, we always get null bssid when repeated notifications are happenning every time. Curiously. – Victor Choy Mar 04 '20 at 10:02
3

SWIFT 4.1 version

I've extend my Reachability class with this functions:

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. Unfortunately there is no additional info about what exactly was changed but we take a chance to test current SSID for example. Hope this will helpful for somebody)

sVd
  • 1,043
  • 12
  • 12
  • In my case the notification get called multiple times when network disconnect or connected. Have you also faced the same issue? – Dinesh Jan 25 '19 at 10:21
  • @Dinesh, I am also having the same issue of "notification get called multiple times". Did you figure out a solution for this? Is it possible to restrict notification get called only once on network change reliably? – Shiva Reddy Jul 24 '19 at 01:49
  • @ShivaReddy Never got a solution for this issue and now no more looking into this. – Dinesh Jul 24 '19 at 10:38
  • As a little improvement to your code: you can use the kNotifySCNetworkChange constant defined in instead of "com.apple.system.config.network_change" directly. – lucianoenrico Jun 30 '20 at 13:16