Search code examples
swiftmacosnsnotificationcentermacos-catalina

DistributedNotificationCenter - How to pass data between applications?


I have built two apps the "Main" app and a Finder Extension which supports it. Using DistributedNotificationCenter I can successfully post messages back and forth between apps and the registered Observer event fires as expected.

The issue seems to be that I cannot pass any user data with the event. All documentation suggests you can pass a NSDictionary or [AnyHashable: Any] object as part of the postNotificationName

Eg:Posting the message looks something like this...

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

Here is my Finder Extension sending code:

    var myInfo = [AnyHashable: Any]()
    myInfo[AnyHashable("filename")] = "Test Data"

    let center: DistributedNotificationCenter = DistributedNotificationCenter.default()
    center.postNotificationName(NSNotification.Name("RequestSyncState"), object: nil, userInfo: myInfo, deliverImmediately: true)

And the Main app receiving code:

    @objc func recievedMessage(notification:NSNotification){
    NSLog ("Message Recieved from Finder Extension \(notification.name.rawValue)")

    if notification.name.rawValue == "RequestSyncState" {
        NSLog ("Message Recieved from Finder to determine the sync icon")
        guard let userInfo = notification.userInfo else
        {
            return
        }

        guard let value = userInfo["Filename"]  else
        {
            NSLog ("Message payload is empty")
            return
        }

        NSLog ("Message payload is \(value)")
    }

As I said these functions fire and the notification is received, just no actual data. If I query notification.userInfo it is nil and if I query notification.object it too is nil.

I tried everything I can think of and I am at a total loss.


Solution

  • 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)
        }
    }