MacOS notifications hidden in notification center when app is focused - objective-c

I'm writing standalone Objective-C code to display notifications on MacOS. Here's what I have so far.
The main logic is in notify.m:
#import <stdio.h>
#import <Cocoa/Cocoa.h>
#import <UserNotifications/UserNotifications.h>
#import <objc/runtime.h>
#interface AppDelegate : NSObject<NSUserNotificationCenterDelegate>
#end
#implementation AppDelegate
- (BOOL)userNotificationCenter:(NSUserNotificationCenter *)center
shouldPresentNotification:(NSUserNotification *)notification {
return YES;
}
#end
int native_show_notification(char *title, char *msg) {
NSApplication *app = [NSApplication sharedApplication];
AppDelegate *appdel = [[AppDelegate alloc] init];
app.delegate = appdel;
NSUserNotificationCenter *nc = [NSUserNotificationCenter defaultUserNotificationCenter];
nc.delegate = appdel;
NSUserNotification *n = [[NSUserNotification alloc] init];
n.title = [NSString stringWithUTF8String:title];
n.informativeText = [NSString stringWithUTF8String:msg];
[NSUserNotificationCenter.defaultUserNotificationCenter deliverNotification:n];
[[NSRunLoop currentRunLoop] runUntilDate:[NSDate dateWithTimeIntervalSinceNow:0.1]];
return 0;
}
int main() {
const int a = 50, b = 150;
char title[a] = "Test\0", msg[b] = "Hello!\0";
native_show_notification(title, msg);
}
Along with the code, we have an Info.plist in the same directory.
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleIdentifier</key>
<string>com.microsoft.VSCode</string>
</dict>
</plist>
The compilation command is simple: cc -framework Cocoa -o app notif.m. The Info.plist is discovered and incorporated automatically.
This code works fine, with one caveat. If the application defined by the package name (in this case com.microsoft.VSCode) is focused, the notification is not "pushed" to the user under default settings [1]. Instead it is hidden in the notification center. When the application in question is not focused, though, the notification is pushed properly. If you want to test this yourself, try compiling and running the code in VSCode, where it seems like nothing happened until you check your notification center. Then set the <string> to something like com.apple.calendar. It'll work.
How can I avoid this and get the notification to display no matter what?
[1] The default notification scheme is "Banner". The problem goes away when the user selects "Alert", but I cannot expect this. I'd like to get it working with Banner too.

Related

How can i receive Messages in voip?

How can I receive Messages in voip? I can get the Voip Token!
I'm writing in Objective C:
I have searched around the internet but can't find any solutions! All I found is Swift, but can't get the token in Swift. In Objective C I can get token but not get Messages from a line.
apn push "<Token>" -c backgroundfetch.pem -m "Hello"
How can I popup a Messages in Objective C? Here is my code:
#import "AppDelegate.h"
#import <UIKit/UIKit.h>
#import <Foundation/Foundation.h>
#import PushKit;
#import UserNotifications;
#interface AppDelegate ()
#end
#implementation AppDelegate
NSString *fcmToken = #"";
// Trigger VoIP registration on launch
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
[self voipRegistration];
return YES;
}
// Register for VoIP notifications
- (void) voipRegistration {
dispatch_queue_t mainQueue = dispatch_get_main_queue();
// Create a push registry object
PKPushRegistry * voipRegistry = [[PKPushRegistry alloc] initWithQueue: mainQueue];
// Set the registry's delegate to self
voipRegistry.delegate = self;
// Set the push type to VoIP
voipRegistry.desiredPushTypes = [NSSet setWithObject:PKPushTypeVoIP];
}
- (void)pushRegistry:(PKPushRegistry *)registry didUpdatePushCredentials:(PKPushCredentials *)credentials forType:(NSString *)type{
if([credentials.token length] == 0) {
NSLog(#"voip token NULL");
return;
}
NSLog(#"PushCredentials: %#", credentials.token);
}
// Handle incoming pushes
- (void)pushRegistry:(PKPushRegistry *)registry didReceiveIncomingPushWithPayload:(PKPushPayload *)payload forType:(NSString *)type {
// Process the received push
NSLog(#"Get");
}
#end
My info.plist:
<key>UIBackgroundModes</key>
<array>
<string>voip</string>
<string>audio</string>
<string>fetch</string>
<string>remote-notification</string>
</array>
In your case the solution is to send a silent push notification. Once the silent push notification arrives, execute your code with background task.
Here is the good explanations for sending a silent push notification.
https://stackoverflow.com/a/36327058/9106403

Use of NSApplication delegate in bundle

I am trying to create a plugin for Unity using Objective-C for an app running on Mac. I need to get the URL when launching my app from a link using an url protocol. I haven't used Objective-C before, so I am having trouble trying to make it work.
I am using an example provided by Unity (download example) and changing the methods to the ones I need to get the URL, but my app crashes on the line nsApplication = [[NSApplication alloc] init]; on the _GetUrl method. I have no idea what I am missing/doing wrong. Also, _GetUrl is the method called from Unity when I want to ask for the url (which is called at the first frame), but I am afraid it might be called after applicationWillFinishLaunching. So where should I actually set the delegate so that applicationWillFinishLaunching happens after the delegate is set?
I use an .h and a .m script and then compile the bundle and import it into Unity as a plugin. This is my code:
PluginUrlHandler.h
#import <Foundation/Foundation.h>
#import <AppKit/AppKit.h>
#interface NSApplicationDelegate : NSObject
{
NSString* urlString;
}
// NSApplication delegate methods
- (void)applicationWillFinishLaunching:(NSNotification *)aNotification;
- (void)handleGetURLEvent:(NSAppleEventDescriptor *)event withReplyEvent:(NSAppleEventDescriptor *)replyEvent;
//Other methods
- (NSString *)getUrl;
#end
PluginUrlHandler.m
#import <Foundation/Foundation.h>
#import "PluginUrlHandler.h"
#implementation NSApplicationDelegate
- (id)init
{
self = [super init];
urlString = #"nourl";
return self;
}
- (void)applicationWillFinishLaunching:(NSNotification *)aNotification
{
NSAppleEventManager *appleEventManager = [NSAppleEventManager sharedAppleEventManager];
[appleEventManager setEventHandler:self
andSelector:#selector(handleGetURLEvent:withReplyEvent:)
forEventClass:kInternetEventClass andEventID:kAEGetURL];
}
- (void)handleGetURLEvent:(NSAppleEventDescriptor *)event withReplyEvent:(NSAppleEventDescriptor *)replyEvent
{
[event paramDescriptorForKeyword:keyDirectObject] ;
NSString *urlStr = [[event paramDescriptorForKeyword:keyDirectObject] stringValue];
urlString = urlStr;
}
- (NSString *)getUrl
{
return urlString;
}
#end
static NSApplicationDelegate* delegateObject = nil;
static NSApplication* nsApplication = nil;
// Helper method to create C string copy
char* MakeStringCopy (const char* string)
{
if (string == NULL)
return NULL;
char* res = (char*)malloc(strlen(string) + 1);
strcpy(res, string);
return res;
}
#if c__plusplus
extern "C" {
#endif
const char* _GetUrl ()
{
if (delegateObject == nil)
delegateObject = [[NSApplicationDelegate alloc] init];
if (nsApplication == nil)
nsApplication = [[NSApplication alloc] init];
[nsApplication setDelegate:delegateObject];
return MakeStringCopy([[delegateObject getUrl] UTF8String]);
}
#if c__plusplus
}
#endif
If your application is crashing, you need to provide some information, such as a stack trace or error message so we can best help. I'm assuming the error you're receiving looks like this:
2016-03-06 10:07:14.388 test[5831:230418] *** Assertion failure in -[NSApplication init], /Library/Caches/com.apple.xbs/Sources/AppKit/AppKit-1404.34/AppKit.subproj/NSApplication.m:1980
2016-03-06 10:07:14.391 test[5831:230418] An uncaught exception was raised
2016-03-06 10:07:14.391 test[5831:230418] Creating more than one Application
You shouldn't create your own NSApplication object. Just use the system one by referencing [NSApplication sharedApplication].
Generally speaking, you shouldn't need an NSApplication (or NSApplicationDelegate) for a plugin, though. The program that's loaded you should already have one, and you don't want to mess with that. Just create a custom NSObject subclass to be your AppleEvent handler. You don't need NSApplication (or it's delegate) at all for this. Any object can be the target of an AppleEvent.
You can't use things like applicationDidFinishLaunching:withOptions: from a plugin in any case. It's too late. The application has long since launched. You'll need to add your AppleEvent handler in some function called from Unity. I'm not particularly familiar with Unity's plugin engine, so I don't know if there's a particular "load" function that gets called automatically (I don't see one in the sample code). You may have to call something yourself. It would have to occur after the plugin is loaded, but before the Get URL Apple Event happens (it's unclear what you expect to generate that).
Just curious what you're trying to pull off with this. I've never seen a protocol handler used this way.
Creation of NSApplication instance looks very suspicious. Normally you don't create it as it is a singleton by definition.
So instead of this:
if (nsApplication == nil)
nsApplication = [[NSApplication alloc] init];
you should have rather this (getting current NSApplication instance):
if (nsApplication == nil)
nsApplication = [NSApplication sharedApplication];
I made a tutorial on this, see Override app delegate in Unity for iOS and OSX (4/4) Inject code to Mach-O binary.
It uses code injection to set an Objective-C class to respond the corresponding Apple Event.

Mac custom protocol fails on some machines

Mac OS 10.8.3. This simple app runs from a custom protocol when clicking a link in the browser, for example run .
Compiled the .app with xCode, unsigned.
Works on most machines but not on some. One that doesn't work is Mac OS 10.8.2 with the gatekeeper OFF. It gives the error "failed for weird reason (13)". I guess this has got to do with permission or security. I tried chmoding Contents/MacOS/Binary to 777, but still the same.
Do I have to sign it will Apple dev certificate to make it work, or do something else in the code or plist to make it work on all machines?
plist
<key>CFBundleURLTypes</key>
<array>
<dict>
<key>CFBundleURLSchemes</key>
<array>
<string>protocoltest</string>
</array>
<key>CFBundleURLName</key>
<string>com.TestWebLauncher</string>
</dict>
</array>
AppDelegate.m
#import "AppDelegate.h"
#implementation AppDelegate
#synthesize window = _window;
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification
{
if ([invokeUrl length] == 0)
{
invokeUrl = #"no url";
}
[txt setStringValue:invokeUrl];
}
- (id)init
{
[[NSAppleEventManager sharedAppleEventManager] setEventHandler:self andSelector:#selector(handleURLEvent:withReplyEvent:) forEventClass:kInternetEventClass andEventID:kAEGetURL];
return [super init];
}
- (BOOL)handleURLEvent:(NSAppleEventDescriptor*)event withReplyEvent:(NSAppleEventDescriptor*)replyEvent
{
NSString* url = [[event paramDescriptorForKeyword:keyDirectObject] stringValue];
NSLog(#"%#", url);
invokeUrl = url;
return YES;
}
#end

Uncaught exception: ReferenceError: 'NSApplication' is undefined

the code shown below gives an exeption
#import <Foundation/Foundation.h>
#import <AppKit/AppKit.h>
#interface Main : NSObject { }
#end
#implementation Main
+(void)main
{
NSLog(#"Hello world!");
NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
NSApplication *anApplication = nil;
NSArray *screenArray = nil;
NSEnumerator *screenEnumerator = nil;
NSScreen *aScreen = nil;
NSWindowDepth *depths = null;//remove const
BOOL exactMatch = NO;
anApplication = [NSApplication sharedApplication];
screenArray = [NSScreen screens];
screenEnumerator = [screenArray objectEnumerator];
}
#end
i am using windows and please go through the link below qckapp.com/index.html?p=ObjC
you cannot done it with http://qckapp.com/index.html?p=ObjC
just go through the compiler http://www.gnustep.org
Try
#import <Cocoa/Cocoa.h>
I don't think your imports include the header for NSApplication.
That was wrong. Actually, I think the problem is that you haven't called NSApplicationMain(). It doesn't really make sense to have an application object if you don't have an application (with a user interface). If you don't want a UI, you might get away with using NSApplicationLoad().
Also make sure you are linking the AppKit framework.
your compiler does not have capabilities for ui
you just go with http://qckapp.com/index.html?p=ObjC only for basics
try gnustep
good luck.

How to use custom URL scheme to send Get URL event to running application?

My Cocoa application needs to handle Get URL events. I think I correctly followed the steps in this answer to modify Info.plist and write & register a URL handler method.
Problems:
When I go mytesturl://anything in Safari while my app is running, it pops up a window asking if I want to open my app. And if I say yes, nothing seems to happen except that an icon appears for an instant in the dock. So it might be trying to launch another instance of my app instead of sending a message to the running instance?
When I do the same in Firefox, it pops up a window asking me to choose an application. So custom URL protocols are not expected to work in all browsers?
Info.plist:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
...
<key>CFBundleURLTypes</key>
<array>
<dict>
<key>CFBundleURLName</key>
<string>My test URL</string>
<key>CFBundleURLSchemes</key>
<array>
<string>mytesturl</string>
</array>
</dict>
</array>
...
</dict>
</plist>
Source code:
#import <Cocoa/Cocoa.h>
#include <stdio.h>
#interface Test : NSObject
{
}
- (void)test;
- (void)handleEvent:(NSAppleEventDescriptor *)event withReplyEvent:(NSAppleEventDescriptor *)replyEvent;
#end
#implementation Test
- (void)test
{
NSLog(#"Started...");
char c;
scanf("%c", &c);
}
- (void)handleEvent:(NSAppleEventDescriptor *)event withReplyEvent:(NSAppleEventDescriptor *)replyEvent
{
NSURL *url = [NSURL URLWithString:[[event paramDescriptorForKeyword:keyDirectObject] stringValue]];
NSLog(#"url = %#", url);
}
#end
int main(int argc, char *argv[])
{
Test *app=[[Test alloc] init];
[[NSAppleEventManager sharedAppleEventManager] setEventHandler:app andSelector:#selector(handleEvent:withReplyEvent:) forEventClass:kInternetEventClass andEventID:kAEGetURL];
[app test];
return 0;
}
NSAppleEventManager requires an NSApplication instance in order to function.
Try making Test a subclass of NSApplication, and change main to something like this:
id app = [NSApplication sharedApplication];
[[NSAppleEventManager sharedAppleEventManager] setEventHandler:app andSelector:#selector(handleEvent:withReplyEvent:) forEventClass:kInternetEventClass andEventID:kAEGetURL];
[app run];