问题
I'd like to subscribe to WiFi network changes in my Cocoa application, but I haven't been able to find an appropriate event to subscribe to.
Is there an NSNotificationCenter Notification for WiFi network changes?
回答1:
Not to my knowledge. I would use CoreWLAN to get a list of all WiFi interfaces on the system, and then monitor their status using the SystemConfiguration framework.
Here's a command-line example (error checking niceties elided, ARC required):
#import <Foundation/Foundation.h>
#import <CoreWLAN/CoreWLAN.h>
#import <SystemConfiguration/SystemConfiguration.h>
void wifi_network_changed(SCDynamicStoreRef store, CFArrayRef changedKeys, void *ctx)
{
[(__bridge NSArray *)changedKeys enumerateObjectsUsingBlock:^(NSString *key, NSUInteger idx, BOOL *stop)
{
/* Extract the interface name from the changed key */
NSString *ifName = [key componentsSeparatedByString:@"/"][3];
CWInterface *iface = [CWInterface interfaceWithName:ifName];
NSLog(@"%@ status changed: current ssid is %@, security is %ld",
ifName, iface.ssid, iface.security);
}];
}
int main(int argc, char *argv[])
{
/* Get a list of all wifi interfaces, and build an array of SCDynamicStore keys to monitor */
NSSet *wifiInterfaces = [CWInterface interfaceNames];
NSMutableArray *scKeys = [[NSMutableArray alloc] init];
[wifiInterfaces enumerateObjectsUsingBlock:^(NSString *ifName, BOOL *stop)
{
[scKeys addObject:
[NSString stringWithFormat:@"State:/Network/Interface/%@/AirPort", ifName]];
}];
/* Connect to the dynamic store */
SCDynamicStoreContext ctx = { 0, NULL, NULL, NULL, NULL };
SCDynamicStoreRef store = SCDynamicStoreCreate(kCFAllocatorDefault,
CFSTR("myapp"),
wifi_network_changed,
&ctx);
/* Start monitoring */
SCDynamicStoreSetNotificationKeys(store,
(__bridge CFArrayRef)scKeys,
NULL);
CFRunLoopSourceRef src = SCDynamicStoreCreateRunLoopSource(kCFAllocatorDefault, store, 0);
CFRunLoopAddSource([[NSRunLoop currentRunLoop] getCFRunLoop],
src,
kCFRunLoopCommonModes);
[[NSRunLoop currentRunLoop] run];
}
回答2:
As long as you are holding open a CWInterface, you can use the CWLinkDidChangeNotification et al notifications. In the following code, i_interfaces is an ivar which stores an array of CWInterface, monitorWifi should be called at the start, listInterfaces lists the interface values, and handleInterfaceNotification is called whenever anything changes.
Note that iface.ssid / iface.bssid will be nil when the interface is not connected.
Also note that handleInterfaceNotification will be called multiple times for each connect/disconnect due to the various notifications.
This code comes largely from NSNotification troubles
-(void) listInterfaces;
{
NSLog(@"listInterfaces");
[i_interfaces enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop)
{
CWInterface *iface = obj;
NSLog( @"iface %@, SSID %@, BSSID %@", iface, iface.ssid, iface.bssid );
}];
}
-(void) handleInterfaceNotification:(NSNotification*) notification;
{
NSLog(@"Notification Received");
[self listInterfaces];
}
- (void) monitorWifi;
{
NSLog(@"monitorWifi");
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(handleInterfaceNotification:) name:CWModeDidChangeNotification object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(handleInterfaceNotification:) name:CWSSIDDidChangeNotification object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(handleInterfaceNotification:) name:CWBSSIDDidChangeNotification object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(handleInterfaceNotification:) name:CWCountryCodeDidChangeNotification object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(handleInterfaceNotification:) name:CWLinkDidChangeNotification object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(handleInterfaceNotification:) name:CWPowerDidChangeNotification object:nil];
NSMutableArray* ifaces = [NSMutableArray new];
NSSet *wifiInterfaces = [CWInterface interfaceNames];
[wifiInterfaces enumerateObjectsUsingBlock:^(NSString *ifName, BOOL *stop)
{
CWInterface *iface = [CWInterface interfaceWithName:ifName];
if ( iface ) {
[ifaces addObject:iface];
}
}];
i_interfaces = ifaces;
[self listInterfaces];
}
回答3:
Highly appreciated. I needed to do this from Swift so here is a bit of Swift code to get you started:
func callback(store: SCDynamicStore, changedKeys: CFArray,
context: UnsafeMutableRawPointer?) -> Void {
// Do the magic
}
// Connect to the store
guard let store = SCDynamicStoreCreate(nil, "myapp" as CFString, callback, nil) else {
print("Could not connect to store")
abort()
}
// Setup the notification mechanism for any IPv4 event
var keys = [CFString]()
keys.append("State:/Network/Global/IPv4" as CFString)
SCDynamicStoreSetNotificationKeys(store, keys as CFArray, nil)
// Go into the loop
let runloop = SCDynamicStoreCreateRunLoopSource(nil, store, 0)
let currentLoop = RunLoop.current.getCFRunLoop()
let modes = CFRunLoopMode.commonModes
CFRunLoopAddSource(currentLoop, runloop, modes)
RunLoop.current.run()
来源:https://stackoverflow.com/questions/15047338/is-there-a-nsnotificationcenter-notification-for-wifi-network-changes