I have an OS X application that needs to respond to a volume being mounted or unmounted.
I've already solved this problem by retrieving the list of volumes periodically and checking for changes, but I'd like to know if there is a better way.
Register to the notification center you get from [[NSWorkspace sharedWorkspace] notificationCenter] and then process the notifications you are interested in. These are the volume related ones: NSWorkspaceDidRenameVolumeNotification, NSWorkspaceDidMountNotification, NSWorkspaceWillUnmountNotification and NSWorkspaceDidUnmountNotification.
The NSWorkspace approach is exactly the kind of thing I was looking for. A few lines of code later, I have a much better solution than using a timer.
-(void) monitorVolumes
{
[[[NSWorkspace sharedWorkspace] notificationCenter] addObserver:self selector: #selector(volumesChanged:) name:NSWorkspaceDidMountNotification object: nil];
[[[NSWorkspace sharedWorkspace] notificationCenter] addObserver:self selector: #selector(volumesChanged:) name:NSWorkspaceDidUnmountNotification object:nil];
}
-(void) volumesChanged: (NSNotification*) notification
{
NSLog(#"dostuff");
}
Swift 4 version:
Declare NSWorkspace in applicationDidFinishLaunching and add observers for mount and unmount events.
let workspace = NSWorkspace.shared
workspace.notificationCenter.addObserver(self, selector: #selector(didMount(_:)), name: NSWorkspace.didMountNotification, object: nil)
workspace.notificationCenter.addObserver(self, selector: #selector(didUnMount(_:)), name: NSWorkspace.didUnmountNotification, object: nil)
Capture mount and unmount events in:
#objc func didMount(_ notification: NSNotification) {
if let devicePath = notification.userInfo!["NSDevicePath"] as? String {
print(devicePath)
}
}
#objc func didUnMount(_ notification: NSNotification) {
if let devicePath = notification.userInfo!["NSDevicePath"] as? String {
print(devicePath)
}
}
It will print device path e.g /Volumes/EOS_DIGITAL
Here are the constants you can read from userInfo.
NSDevicePath,
NSWorkspaceVolumeLocalizedNameKey
NSWorkspaceVolumeURLKey
Do you know SCEvents? It allows you to be notified when the contents of an observed folder change (/Volumes). This way you don't have to use a timer to periodically check the contents.
Related
I have an application, written in Objective-C for MacOS 10.10+ which registers for sleep/wake notifications (code sample below, but the code isn't the question). What I am wondering is, if I call IORegisterForSystemPower at App initialisation, but during debugging I kill the app before it has a chance to call IODeregisterForSystemPower, what are the implications? Does the app get de-registered automatically when it dies in any case? Is there a system dictionary I need to clear out (a plist somewhere, etc.)? Thanks in advance for any help.
io_object_t root_notifier = MACH_PORT_NULL;
IONotificationPortRef notify = NULL;
DebugLog(#"App: Logging IORegisterForSystemPower sleep/wake notifications %#", [NSDate date]);
/* Log sleep/wake messages */
powerCallbackPort = IORegisterForSystemPower ((__bridge void *)self, ¬ify, sleepWakeCallback, &root_notifier);
if ( powerCallbackPort == IO_OBJECT_NULL ) {
DebugLog(#"IORegisterForSystemPower failed");
return;
}
self.rootNotifierPtr = &(root_notifier); // MARK: deregister with this pointer
if ( notify && powerCallbackPort )
{
CFRunLoopAddSource(CFRunLoopGetCurrent(),IONotificationPortGetRunLoopSource(notify), kCFRunLoopDefaultMode);
}
To be honest, I don't know the exact answer. But it may help you.
First of, if you call IORegisterForSystemPower, you need to make two calls in this order: - Call IODeregisterForSystemPower with the 'notifier' argument returned here. - Then call IONotificationPortDestroy passing the 'thePortRef' argument returned here (Please visit apple's document for more detail).
In case of port binding, if I use CFSocketSetAddress, before releasing this socket no other can use this port for binding. But in case of app terminate/closed without releasing this socket this port is available. That means after terminated the app system automatically releasing this.
Does the app get de-registered automatically when it dies in any case?
I think it will automatically de-registered by system.
I also used similar code as you in one of my project. But recently replaced with below codes:
[[[NSWorkspace sharedWorkspace] notificationCenter] addObserver: self selector: #selector(receiveWakeNotification:) name: NSWorkspaceDidWakeNotification object: nil];
[[[NSWorkspace sharedWorkspace] notificationCenter] addObserver: self selector: #selector(receiveSleepNotification:) name: NSWorkspaceWillSleepNotification object: nil];
I am developing an app in Xcode on Mac and would like to know the event which is fired when the mac gets back from sleep.
AwakeFromNib doesn't seem to work.
For swift 3:
func onWakeNote(note: NSNotification) {
print("Received wake note: \(note.name)")
}
func onSleepNote(note: NSNotification) {
print("Received sleep note: \(note.name)")
}
func fileNotifications() {
NSWorkspace.shared().notificationCenter.addObserver(
self, selector: #selector(onWakeNote(note:)),
name: Notification.Name.NSWorkspaceDidWake, object: nil)
NSWorkspace.shared().notificationCenter.addObserver(
self, selector: #selector(onSleepNote(note:)),
name: Notification.Name.NSWorkspaceWillSleep, object: nil)
}
For swift 4:
#objc func onWakeNote(note: NSNotification) {
...
}
#objc func onSleepNote(note: NSNotification) {
...
}
func fileNotifications() {
NSWorkspace.shared.notificationCenter.addObserver(
self, selector: #selector(onWakeNote(note:)),
name: NSWorkspace.didWakeNotification, object: nil)
NSWorkspace.shared.notificationCenter.addObserver(
self, selector: #selector(onSleepNote(note:)),
name: NSWorkspace.willSleepNotification, object: nil)
}
Just found it:
- (void) receiveWakeNote: (NSNotification*) note
{
NSLog(#"receiveSleepNote: %#", [note name]);
}
- (void) fileNotifications
{
//These notifications are filed on NSWorkspace's notification center, not the default
// notification center. You will not receive sleep/wake notifications if you file
//with the default notification center.
[[[NSWorkspace sharedWorkspace] notificationCenter] addObserver: self
selector: #selector(receiveWakeNote:)
name: NSWorkspaceDidWakeNotification object: NULL];
}
You can use IORegisterForSystemPower().
Connects the caller to the Root Power Domain IOService for the purpose
of receiving sleep & wake notifications for the system. Does not
provide system shutdown and restart notifications.
io_connect_t IORegisterForSystemPower (
void *refcon,
IONotificationPortRef *thePortRef,
IOServiceInterestCallback callback,
io_object_t *notifier ) ;
Take a look at Q:How can my application get notified when the computer is going to sleep or waking from sleep? How to I prevent sleep?
I have successfully turned on the setSubjectAreaChangeMonitoringEnabled:
self.videoDevice = [AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeVideo];
[self.videoDevice lockForConfiguration:nil]; //you must lock before setting torch mode
[self.videoDevice setSubjectAreaChangeMonitoringEnabled:YES];
[self.videoDevice unlockForConfiguration];
in start method I have included the following notification:
[[NSNotificationCenter defaultCenter] addObserver: self
selector: #selector(areWeFocused:)
name: AVCaptureDeviceSubjectAreaDidChangeNotification
object: nil];
Then I have the areWeFocused method (which I know is wrong but I need help with it):
- (void)areWeFocused:(NSNotification *) notification {
BOOL adjusting = [self.videoDevice isAdjustingFocus];
if (!adjusting) {
//NSLog(#"I have focus");
} else {
NSLog(#"NOT");
}
}
If uncommented my NSLog always prints I have focus, but commenting that line I never get NOT. How can I detect if we are in focus? BTW: Focus mode is AutoFocus and ContinuousAutoFocus
Thank you for your help.
I am fresher for this application development field. I am tried to get notifications during user login & logout events. I have tried with NSWorkSpaceNotifications, but it does not working for me.
Can somebody help me please.
-(void)logInLogOutNotifications{
NSNotificationCenter *notCenter;
notCenter = [[NSWorkspace sharedWorkspace] notificationCenter];
[notCenter addObserver:self
selector:#selector(observerMethod)
name:NSWorkspaceWillPowerOffNotification object:nil];
}
-(void)observerMethod:(NSNotification *)senderNotification;{
NSLog(#"System Logout Notification is called***********************");
}
NSApplicationMain Begin the RunLoop. You are calling logInLogOutNotifications function from main() function, so you shoud run runloop. or call logInLogOutNotifications in applicationDidFinishLaunching
-(void)logInLogOutNotifications{
NSNotificationCenter *notCenter;
notCenter = [[NSWorkspace sharedWorkspace] notificationCenter];
[notCenter addObserver:self
selector:#selector(observerMethod)
name:NSWorkspaceWillPowerOffNotification object:nil];
[[NSRunLoop currentRunLoop] run];
}
Your method takes one argument, so you should change
#selector(observerMethod)
to
#selector(observerMethod:)
If you're like me and having problems figuring out why in the world you are not getting receiving the NSWorkspaceWillPowerOffNotification notification, make sure you are setting yourself as an observer of [[NSWorkspace sharedWorkspace] notificationCenter] and -NOT- [NSNotificationCenter defaultCenter]!
I spent the better part of a day trying to debug why I wasn't getting that notification because I didn't read the answer clearly!
If you want to know when user resign or become active you just should subscribe to the following notifications:
Swift 3
NSWorkspace.shared().notificationCenter.addObserver(self, selector: #selector(sessionResignActive(_:)), name: NSNotification.Name.NSWorkspaceSessionDidResignActive, object: nil)
NSWorkspace.shared().notificationCenter.addObserver(self, selector: #selector(sessionBecomeActive(_:)), name: NSNotification.Name.NSWorkspaceSessionDidBecomeActive, object: nil)
I'm new to Obj-c. I've got a class which sets a var boolean to YES if it's successful (Game Center login = successful), what it would be great to do, is somehow have a listener to that var that listens to when it is YES and then executes some code. Do I use a block for that? I'm also using the Sparrow framework.
Here's my code in my GameCenter.m file
-(void) setup
{
gameCenterAuthenticationComplete = NO;
if (!isGameCenterAPIAvailable()) {
// Game Center is not available.
NSLog(#"Game Center is not available.");
} else {
NSLog(#"Game Center is available.");
__weak typeof(self) weakSelf = self; // removes retain cycle error
GKLocalPlayer *localPlayer = [GKLocalPlayer localPlayer]; // localPlayer is the public GKLocalPlayer
__weak GKLocalPlayer *weakPlayer = localPlayer; // removes retain cycle error
weakPlayer.authenticateHandler = ^(UIViewController *viewController, NSError *error)
{
if (viewController != nil)
{
[weakSelf showAuthenticationDialogWhenReasonable:viewController];
}
else if (weakPlayer.isAuthenticated)
{
[weakSelf authenticatedPlayer:weakPlayer];
}
else
{
[weakSelf disableGameCenter];
}
};
}
}
-(void)showAuthenticationDialogWhenReasonable:(UIViewController *)controller
{
[[[[[UIApplication sharedApplication] delegate] window] rootViewController] presentViewController:controller animated:YES completion:nil];
}
-(void)authenticatedPlayer:(GKLocalPlayer *)player
{
NSLog(#"%#,%#,%#",player.playerID,player.displayName, player.alias);
gameCenterAuthenticationComplete = YES;
}
-(void)disableGameCenter
{
}
But I need to know from a different object if that gameCenterAuthenticationComplete equals YES.
You can use a delegate pattern. It's far easier to use than KVO or local notifications and it's used a lot in Obj-C.
Notifications should be used only in specific situations (e.g. when you don't know who wants to listen or when there are more than 1 listeners).
A block would work here but the delegate does exactly the same.
You could use KVO (Key-Value Observing) to watch a property of your object, but I'd rather post a NSNotification in your case.
You'll need to have the objects interested in knowing when Game Center login happened register themselves to NSNotificationCenter, then post the NSNotification in your Game Center handler. Read the Notification Programming Topics for more details !
If there is a single method to execute on a single delegate object, you can simply call it in the setter. Let me give a name to this property:
#property(nonatomic,assign, getter=isLogged) BOOL logged;
It's enough that you implement the setter:
- (void) setLogged: (BOOL) logged
{
_logged=logged;
if(logged)
[_delegate someMethod];
}
Another (suggested) way is to use NSNotificationCenter. With NSNotificationCenter you can notify multiple objects. All objects that want to execute a method when the property is changes to YES have to register:
NSNotificationCenter* center=[NSNotificationCenter defaultCenter];
[center addObserver: self selector: #selector(handleEvent:) name: #"Logged" object: nil];
The handleEvent: selector will be executed every time that logged changes to YES. So post a notification whenever the property changes:
- (void) setLogged: (BOOL) logged
{
_logged=logged;
if(logged)
{
NSNotificationCenter* center=[NSNotificationCenter defaultCenter];
[center postNotificationName: #"Logged" object: self];
}
}