I'm trying to implement push notifications on MacOS within a C codebase. Ideally, there'd just be one Objective-C file containing (1) a public C function I can call and (2) some Objective-C code I can use to throw a notification. This way, the source files can be compiled and linked seamlessly in the build process.
Toward this end, I've been trying to create a minimal example that can throw notifications with just a single .m file (not an entire XCode project), much like the one discussed in NSUserNotificationCenter not showing notifications. However, two problems:
- I still can't get the code to work despite trying the solutions in the aforementioned link. It compiles and runs but does not throw a notification.
- If possible, we'd like to switch to the new user notifications API. Not a big deal if this isn't possible for now, though.
Here's what I've tried so far:
#import <Foundation/Foundation.h>
#import <Foundation/NSUserNotification.h>
@interface AppDelegate : NSObject <NSUserNotificationCenterDelegate>
@end
@implementation AppDelegate
- (BOOL)userNotificationCenter:(NSUserNotificationCenter *)center
shouldPresentNotification:(NSUserNotification *)notification {
return YES;
}
- (void)throwNotification {
NSUserNotification *userNotification = [[NSUserNotification alloc] init];
userNotification.title = @"Some title";
userNotification.informativeText = @"Some text";
printf("trying to throw {%s %s}\n", [[userNotification title] UTF8String], [[userNotification informativeText] UTF8String]);
[[NSUserNotificationCenter defaultUserNotificationCenter] deliverNotification:userNotification];
}
@end
int main (int argc, const char * argv[]) {
AppDelegate *app = [[AppDelegate alloc] init];
[app throwNotification];
return 0;
}
This is compiled with cc -framework Foundation -o app main.m.
Any insight would be appreciated!
CodePudding user response:
The problem is that in order to display a notification, you need to have a proper bundle identifier.
We're going to take a bit of the code from here, where we wait for the notification to get displayed. We can embed an Info.plist file into the compiled binary, which will accomplish the same thing as the swizzling code in a file called notify.m:
#import <Foundation/Foundation.h>
#import <Cocoa/Cocoa.h>
@interface AppDelegate : NSObject<NSUserNotificationCenterDelegate>
@property (nonatomic, assign) BOOL keepRunning;
@end
int main (int argc, const char * argv[])
{
@autoreleasepool {
NSApplication *app = [NSApplication sharedApplication];
AppDelegate *appdel = [[AppDelegate alloc] init];
app.delegate = appdel;
NSUserNotificationCenter *nc = [NSUserNotificationCenter defaultUserNotificationCenter];
nc.delegate = appdel;
appdel.keepRunning = TRUE;
NSUserNotification *userNotification = [[NSUserNotification alloc] init];
userNotification.title = @"Some title";
userNotification.informativeText = @"Some text";
[[NSUserNotificationCenter defaultUserNotificationCenter] deliverNotification:userNotification];
while (appdel.keepRunning) {
[[NSRunLoop currentRunLoop] runUntilDate:[NSDate dateWithTimeIntervalSinceNow:0.1]];
}
}
return 0;
}
@implementation AppDelegate
- (BOOL)userNotificationCenter:(NSUserNotificationCenter *)center
shouldPresentNotification:(NSUserNotification *)notification {
return YES;
}
- (void)userNotificationCenter:(NSUserNotificationCenter *)center didDeliverNotification:(NSUserNotification *)notification
{
self.keepRunning = NO;
}
@end
I construct an Info.plist file consisting of the following:
<?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.apple.finder</string>
</dict>
</plist>
I then compile it using:
clang -o notify -framework Cocoa notify.m -Wl,-sectcreate,_\_TEXT,__info_plist,Info.plist
there's a
\in that build line to avoid the markdown parsing of the underscores, but it's not needed for the actual build command line.
