Distributednotificationcenter - How to Pass Data Between Applications

DistributedNotificationCenter - How to pass data between applications?

For anyone who stumbles on this, it turns out the only way I could make it work was to post any of my own data as part of the message was to include it in the Object property on the postNotificationName method. UserInfo is completely ignored across the process boundaries.

So I serialised my own classs CacheEntry into a string, post it, then unwrap it on the other side.

So something like:

func sendMessage(name: String, data:CacheEntry) {

    let message:String = createMessageData(messsagePayload: data)

let center: DistributedNotificationCenter = DistributedNotificationCenter.default()
center.postNotificationName(NSNotification.Name(name), object: message, userInfo: nil, deliverImmediately: true)
}

func createMessageData(messsagePayload:CacheEntry) -> String {

    let encoder = JSONEncoder()
let data = try! encoder.encode(messsagePayload)

let messsagePayloadString = String(data: data, encoding: .utf8)!

return String(messsagePayloadString)
}

func reconstructEntry(messagePayload:String) -> CacheEntry {

    let jsonData = messagePayload.data(using: .utf8)!
let messsagePayloadCacheEntry = try! JSONDecoder().decode(CacheEntry.self, from: jsonData)

return messsagePayloadCacheEntry

}

@objc func recievedMessage(notification:NSNotification){

NSLog ("Message Received from Application \(notification.name)")

if notification.name.rawValue == "DSF-SetSyncState" {

NSLog ("Message Recieved from Application to set the sync icon")

let cEntry = reconstructEntry(messagePayload: notification.object as! String)

}


}

How to observe notifications from a different application?

Catalina silently changed the addObserver behavior - you can no longer use a nil value for the name to observe all notifications - you have to pass in a name. This makes discovery of event names more difficult.

Firstly, you have to understand that while the NSDistributedNotificationCenter has the word Notification in it; it's not related. From the About Local Notifications and Remote Notifications, it does state:

Note: Remote notifications and local notifications are not related to broadcast notifications (NSNotificationCenter) or key-value observing notifications.

So, at that point, I'm going to answer from the perspective of NSDistributedNotificationCenter, and not about Remote/Local notifications - you already have a potential solution in the answer you linked for observing the DB file that contains a record of the notifications in that way.

This example code will not work on Catalina (10.15) because of API behavior changes

The first thing you need to do is listen for the correct notification. Creating a simple application that listens for all events; e.g. using:

NSDistributedNotificationCenter *center = [NSDistributedNotificationCenter defaultCenter];
[center addObserver:self
selector:@selector(notificationEvent:)
name:nil
object:nil];

-(void)notificationEvent:(NSNotification *)notif {
NSLog(@"%@", notif);
}

It indicates that the notifications are:

__CFNotification 0x6100000464b0 {name = com.airserverapp.AudioDidStart; object = com.pratikkumar.airserver-mac; userInfo = {
ip = "2002:d55e:dbb2:1:10e0:1bfb:4e81:b481";
remote = YES;
}}
__CFNotification 0x618000042790 {name = com.airserverapp.AudioDidStop; object = com.pratikkumar.airserver-mac; userInfo = {
ip = "2002:d55e:dbb2:1:10e0:1bfb:4e81:b481";
remote = YES;
}}

This indicates that the name parameter in the addObserver call should be com.airserverapp.AudioDidStart and com.airserverapp.AudioDidStop.

You can use code like that to determine all notifications, which will allow you to add the relevant observers when you want the particular observer. This is probably the easiest way to observe notifications of this type.

Passing Data Indirectly to an Unrelated NSViewController

With the caveat that "globals and singletons should be avoided," it sounds like they are a good fit for what you're trying to do. As you get more comfortable in Cocoa you can move into more sophisticated means of accomplishing this (dependency injection). Look into this as you get more comfortable with Cocoa.

Create a simple singleton type:

// AppState.swift

class AppState {
// Basic singleton setup:
static let shared = AppState()
private init() {} // private prevents instantiating it elsewhere

// Shared state:
var carrierArray: [String] = []
}

Access it from your view controllers:

// YourViewController.swift:

@IBAction func doSomething(_ sender: Any) {
AppState.shared.carrierArray = ...
}

If you need to update the other view controllers when this shared state changes, notifications are a good tool for that. You could do this with a didSet on carrierArray, or simply trigger the notification manually.

how to pass parameters between cocoa applications

I did this using NSWorkspace to launch the app, and NSDistributedNotificationCenter to pass the data. This obviously isn't fully developed, but it worked. One caveat from the docs -- the dictionary I sent with the argument (just a string in this example) can't be used in a sandboxed app (the dictionary must be nil).

This is in the app that opens the other app:

- (void)applicationDidFinishLaunching:(NSNotification *)aNotification 
{
ws = [NSWorkspace sharedWorkspace];
NSNotificationCenter *center = [ws notificationCenter];
[center addObserver:self selector:@selector(poster:) name:NSWorkspaceDidLaunchApplicationNotification object:nil];
[ws launchApplication:@"OtherApp.app"];
}

-(void)poster:(NSNotification *) aNote
{
NSDistributedNotificationCenter *center = [NSDistributedNotificationCenter defaultCenter];
NSDictionary *dict = [NSDictionary dictionaryWithObject:@"theDataToSend" forKey:@"startup"];
[center postNotificationName:@"launchWithData" object:nil userInfo:dict];
NSLog(@"Posted notification");
}

And this is in the app that is opened:

-(void)awakeFromNib 
{
NSDistributedNotificationCenter *center = [NSDistributedNotificationCenter defaultCenter];
[center addObserver:self selector:@selector(doStartup:) name:@"launchWithData" object:nil];
}

-(void)doStartup:(NSNotification *) aNote
{
NSLog(@"%@",aNote.userInfo);
}

Distributed notification no longer working in Catalina

The XPC will not work in this case. Communication with new type Safari Extensions should be performed via SFSafariExtensionHandler subclass.

It is recommended to create Safari Extension via Xcode corresponding template, all important settings, Info.plist fields, and infrastructure will be set up for you.

Please see good documented details at official Building a Safari App Extension

Can NSNotificationCenter be used between different apps to communicate?

Sorry for a brief answer but in this case that's all there is to it - no they cannot.

You can register to handle a URL type and then launch pass some data that way.

Cocoa NSNotificationCenter communication between apps failed

I solved my problem. This is the source code:
In the client side:

NSDictionary *user = [NSDictionary dictionaryWithObjectsAndKeys: @"John Doe", @"name", @"22222222222", @"phone", @"Dummy address", @"address", nil];

//Post the notification
NSString *observedObject = @"com.test.net";
NSDistributedNotificationCenter *center = [NSDistributedNotificationCenter defaultCenter];
[center postNotificationName: @"Plugin Notification" object: observedObject userInfo: user deliverImmediately: YES];

In the server side

NSString *observedObject = @"com.test.net";
NSDistributedNotificationCenter *center = [NSDistributedNotificationCenter defaultCenter];
[center addObserver: self selector: @selector(receiveNotification:) name: @"Plugin Notification" object: observedObject];

receiveNotification definition

-(void)receiveNotification:(NSNotification*)notif
{
NSlog(@"Hello");
}

In dealloc method

[[NSDistributedNotificationCenter defaultCenter] removeObserver:self name: @"Plugin Notification" object: nil];


Related Topics



Leave a reply



Submit