Reputation: 137
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:
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!
Upvotes: 2
Views: 921
Reputation: 94654
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>
Please use an appropriate bundle identifier, as
com.apple.finder
will cause all these notifications to appear to come from macOS Finder, which might be confusing to users. 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.
Upvotes: 3