This question already has an answer here:
How could I get notifications in my app when a different app starts on Mac OS X?
(1 answer)
Closed 10 years ago.
Is there a way, in either Applescript or Objective-C, to detect when a certain application opens? My goal is to add a feature to an application that I'm working on to show a message whenever "QuickTime Player" opens, but I haven't found a anything in the Apple developer documents that shows how to do anything like this.
This is pretty simple with Objective-C. Here's the code:
Register for the proper notifications from NSWorkspace:
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification
{
//Fetch the notification center from the workspace
NSNotificationCenter* center = [[NSWorkspace sharedWorkspace] notificationCenter];
[center addObserver:self selector:#selector(newApplicationDidLaunch:) name:NSWorkspaceDidLaunchApplicationNotification object:nil];
[center addObserver:self selector:#selector(newApplicationWillLaunch:) name:NSWorkspaceWillLaunchApplicationNotification object:nil];
}
Then, add your selectors for the notification. The userInfo dictionary of the notification will hold everything you need to know:
-(void)newApplicationDidLaunch:(NSNotification*)notification {
NSDictionary* userInfo = notification.userInfo;
//Do what you want here after application launch.
}
-(void)newApplicationWillLaunch:(NSNotification*)notification {
NSDictionary* userInfo = notification.userInfo;
//Do what you want here to prepare for application launch.
}
Hope that helps.
Related
I have made the ExtensionDelegate my UNUserNotificationCenterDelegate and properly assigned it. I have my watchOS 3 app add a notification request, which triggers
userNotificationCenter:willPresentNotification:withCompletionHandler:
I have implemented the method as follows:
- (void) userNotificationCenter:(UNUserNotificationCenter*)center
willPresentNotification:(UNNotification*)notification
withCompletionHandler:(void(^)(UNNotificationPresentationOptions))completionHandler
{
NSLog(#"ExtensionDelegate: willPresent!");
completionHandler(UNNotificationPresentationOptionAlert);
}
The problem is that nothing happens. I'm debugging in Xcode 8.2 on a device running watchOS 3.1, and though this delegate method is triggered, and I can hit the breakpoint, no alert is shown. My content is as follows:
UNMutableNotificationContent* content = [[UNMutableNotificationContent alloc] init];
content.title = notificationType;
content.subtitle = [NSString stringWithFormat:#"Subtitle: %#", notificationType];
content.body = alertTitle;
content.badge = #1;
content.sound = [UNNotificationSound defaultSound];
content.launchImageName = #"LaunchImage";
content.userInfo = #{
#"notificationType" : notificationType,
#"count" : #(count)
};
content.attachments = #[];
content.categoryIdentifier = notificationType;
where notificationType is the NSString I set on my application's launch as follows:
// Register custom notification types
UNNotificationCategory* cat1 = [UNNotificationCategory categoryWithIdentifier:#"Category1"
actions:#[dismissAction]
intentIdentifiers:#[]
options:UNNotificationCategoryOptionNone];
UNNotificationCategory* cat2 = [UNNotificationCategory categoryWithIdentifier:#"Category2"
actions:#[dismissAction, yesAction]
intentIdentifiers:#[]
options:UNNotificationCategoryOptionNone];
NSSet* categorySet = [NSSet setWithArray:#[cat1, cat2]];
[center setNotificationCategories:categorySet];
Unfortunately, when the notification request is made, willPresentNotification comes and goes, the completionHandler gets called, and then the app just keeps on running in the foreground. No notification alert appears on screen. It doesn't appear in the drag-down dock on the watch face, either. What else do I need to do to have it appear, either on-screen then and there, or into the pull-down dock?
Here is a visual example of what I'd expect to see on top of the app, or flush-top with the the top of the screen,with the app running below it. The banner can be even thinner than shown here:
Simple watchOS Notification
Related question, but for iOS: Displaying a stock iOS notification banner when your app is open and in the foreground?
I have been trying the same thing on the watch and get the same (non-displayed notification) result. One note, in case it helps others, is that we have to ask for the same permission as we return in the completion handler. For example, if we want to show Alert notifications, we have to use the two corresponding values as shown below. As the OP noted, we have to set ourself as the notification center delegate.
(BTW, if I've got any errors here, please point them out - mine is not working yet either).
The following code is in the watchkit extension delegate.
- (void)applicationDidFinishLaunching {
// ...
// Register for, and request permission to send notifications
UNUserNotificationCenter* center = [UNUserNotificationCenter currentNotificationCenter];
center.delegate = self;
[center requestAuthorizationWithOptions:(UNAuthorizationOptionAlert)
completionHandler:^(BOOL granted, NSError * _Nullable error) {
// Enable or disable features based on authorization.
NSLog(#"Got auth response: %d", granted);
}];
}
The above is successful only when I have the notification permissions set to "Mirror the iPhone". If I turn that off, my permissions request fails. A different problem, but please check the returned granted value and make sure it's true.
// Foreground notification
- (void)userNotificationCenter:(UNUserNotificationCenter *)center
willPresentNotification:(UNNotification *)notification
withCompletionHandler:(void (^)(UNNotificationPresentationOptions options))completionHandler {
// The method will be called on the delegate only if the
// application is in the foreground.
NSLog(#"Will present notification: %#", notification);
// The following doesn't present the notification, though.
completionHandler(UNNotificationPresentationOptionAlert);
}
The userNotificationCenter:willPresentNotification:withCompletionHandler method does get called, but I don't see a notification overlay, nor is there one in the notifications list.
I'm developing kind of a plugin for iTunes.
A lot of user have requested, that they would like to start the plugin if they start iTunes, which of course makes sense. However, I'm not sure how to do this.
I thought about a helper app, which is probably the only way.
The only thing that bothers me is how to get the notification.
Of course I could consistently check if iTunes is running, but I'm not sure if that's the right way to do it.
I would rather add my app as an observer of that process.
Is that possible?
If not, how does Activity Monitor do it?
SOLUTION
Thanks to Daij-Djan! I got it working like this:
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification
{
[[[NSWorkspace sharedWorkspace] notificationCenter] addObserver:self
selector:#selector(iTunesLaunched:)
name:NSWorkspaceDidLaunchApplicationNotification
object:nil];
[[[NSWorkspace sharedWorkspace] notificationCenter] addObserver:self
selector:#selector(iTunesTerminated:)
name:NSWorkspaceDidTerminateApplicationNotification
object:nil];
}
-(void) iTunesLaunched:(NSNotification *)notification {
NSRunningApplication *runApp = [[notification userInfo] valueForKey:#"NSWorkspaceApplicationKey"];
if ([runApp.bundleIdentifier isEqualToString:#"com.apple.iTunes"])
NSLog(#"start");
}
-(void) iTunesTerminated:(NSNotification *)notification {
NSRunningApplication *runApp = [[notification userInfo] valueForKey:#"NSWorkspaceApplicationKey"];
if ([runApp.bundleIdentifier isEqualToString:#"com.apple.iTunes"])
NSLog(#"terminate");
}
register for NSWorkspace notifications:
NSWorkspaceDidLaunchApplicationNotification
NSWorkspaceDidTerminateApplicationNotification
see https://developer.apple.com/library/mac/#documentation/Cocoa/Reference/ApplicationKit/Classes/NSWorkspace_Class/Reference/Reference.html
there is also the possibility to KVO the runningApplications property
btw cocoatech has a nice NTRunningAppManager class that does just that
For example, get the notification that another Application is becoming Active on the screen, or resign active state.
Sure. In your app delegate class, you can use NSWorkspace to get notified when an app becomes active (NSWorkspaceDidActivateApplicationNotification) or resigns active (NSWorkspaceDidDeactivateApplicationNotification). See the documentation on NSWorkspace for more info.
In your controller class, you'd do something like this:
- (id)init {
if ((self = [super init])) {
[[[NSWorkspace sharedWorkspace] notificationCenter] addObserver:self
selector:#selector(appDidActivate:)
name:NSWorkspaceDidActivateApplicationNotification
object:nil];
}
return self;
}
- (void)dealloc {
[[[NSWorkspace sharedWorkspace] notificationCenter] removeObserver:self];
[super dealloc];
}
- (void)appDidActivate:(NSNotification *)notification {
NSDictionary *userInfo = [notification userInfo];
NSLog(#"userInfo == %#", userInfo);
}
The key points are basically that you need to register to receive the notifications like shown in -init. You'd repeat the code to add another observer for each additional notification name that you want (e.g NSWorkspaceDidDeactivateApplicationNotification).
Another important thing to remember is to remove yourself as an observer in -dealloc (or elsewhere), so that NSWorkspace doesn't try to notify your controller object after it's been released+dealloc'd (and would no longer be valid).
In the specified -appDidActivate: method, do whatever you need to with the info about the app in question.
If you want something simpler than distributed objects, you could use distributed notifications from the distributed notification center. However, these are not posted unless you built the application. For monitoring when applications start or quit, you can use NSWorkspace and its notification center (suggested by NSGod)
So I'm new to NSNotifications, I am wondering what the scope is. I.e. If I have an Application Delegate Class, and it is the receiver of a notification:
-(id)init
{
[ super init];
if (!self) return nil;
// Add to our notification
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(receiveUpdateRequest:)
name:#"RequestStatusUpdate"
object:nil];
return self;
}
And has this method run on receive:
- (void) receiveUpdateRequest:(NSNotification *) notification
{
// Check the Notification Name
if ([[notification name] isEqualToString:#"RequestStatusUpdate"]){
NSLog (#"Recieved Update Status!");
}
else {
NSLog(#"Recieved Notification: %#",[notification name]);
}
}
Can I post a notification like so:
[[NSNotificationCenter defaultCenter] postNotificationName:#"RequestStatusUpdate" object:self];
From another object instance any where in my App?
Even for instance an object that instantiated by virtue of a NIB being loaded:
summaryWindow = [[SummaryWindowController alloc] initWithWindowNibName:#"SummaryWindow" owner:globalStatusController];
Do I have to have anything else configured in my summaryWindow Class to be able to call the postNotificationName method.
Or put a different way is the [NSNotificationCenter defaultCenter] global for all instances of all objects in my Application, I would assume thats how its suppose to work but currently when I call this method via an IBAction in my SummaryWindow , the notification does not seemed to be received.
I have tested both [NSThread currentThread] and the default Notification center and it does look like I'm in the thread and the same notification center ( which I think is always global). I am only looking into the thread thing as its come up on a few other threads.
2011-08-22 20:57:11.452 AppName[23102:1307] Using Default Notification Center: <CFNotificationCenter 0x10012c900 [0x7fff7d302ea0]>
2011-08-22 20:57:20.366 AppName[23102:1307] Using Default Notification Center: <CFNotificationCenter 0x10012c900 [0x7fff7d302ea0]>
Wow that was lame, I just found [[NSNotificationCenter defaultCenter] removeObserver:self]; in some earlier code. I had it in dealloc but some how managed to miss it in another NSTask method I was working on.
Or put a different way is the [NSNotificationCenter defaultCenter] global for all instances of all objects in my Application
Yes.
I would assume thats how its suppose to work but currently when I call this method via an IBAction in my SummaryWindow , the notification does not seemed to be received.
That's because you're registering in init. I'm betting this is Mac, and on Mac the application delegate is almost always instantiated from a nib file. You need to do this work in awakeFromNib.
Note that you generally do not need to check the notification's name. It's generally best to have a different method for each notification callback. You should also always create a string constant for your notification names. It's way too easy to mis-type them.
In there a way to do this? I register my object for UIPasteboardChangedNotification at launch time, but when sending it to the background and opening (for instance) Safari and copying some text, my handler never gets called.
(I'm using just the simulator for now).
I've used both:
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(pasteboardNotificationReceived:)
name:UIPasteboardChangedNotification
object:[UIPasteboard generalPasteboard]];
and:
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(pasteboardNotificationReceived:)
name:UIPasteboardChangedNotification
object:nil ];
to register my handler.
I had the same problem. According to the UIPasteboard Class Reference documentation for the changeCount property (emphasis is mine):
Whenever the contents of a pasteboard changes—specifically, when pasteboard items are added, modified, or removed—UIPasteboard increments the value of this property. After it increments the change count, UIPasteboard posts the notifications named UIPasteboardChangedNotification (for additions and modifications) and UIPasteboardRemovedNotification (for removals). ... The class also updates the change count when an application reactivates and another application has changed the pasteboard contents. When users restart a device, the change count is reset to zero.
I had read this to mean that my application would receive UIPasteboardChangedNotification notifications once my app was reactivated. A careful reading reveals, however, that it is only the changeCount that is updated when the app is reactivated.
I dealt with this by tracking the pasteboard's changeCount in my app delegate and posting the expected notification when I find the changeCount has been changed while the app was in the background.
In the app delegate's interface:
NSUInteger pasteboardChangeCount_;
And in the app delegate's implementation:
- (BOOL)application:(UIApplication*)application
didFinishLaunchingWithOptions:(NSDictionary*)launchOptions {
[[NSNotificationCenter defaultCenter]
addObserver:self
selector:#selector(pasteboardChangedNotification:)
name:UIPasteboardChangedNotification
object:[UIPasteboard generalPasteboard]];
[[NSNotificationCenter defaultCenter]
addObserver:self
selector:#selector(pasteboardChangedNotification:)
name:UIPasteboardRemovedNotification
object:[UIPasteboard generalPasteboard]];
...
}
- (void)pasteboardChangedNotification:(NSNotification*)notification {
pasteboardChangeCount_ = [UIPasteboard generalPasteboard].changeCount;
}
- (void)applicationDidBecomeActive:(UIApplication*)application {
if (pasteboardChangeCount_ != [UIPasteboard generalPasteboard].changeCount) {
[[NSNotificationCenter defaultCenter]
postNotificationName:UIPasteboardChangedNotification
object:[UIPasteboard generalPasteboard]];
}
}