[macOS][Swift4.2] Distributed Notifications でアプリ間通信


Macアプリでコマンドラインツールやアプリを内包してサブタスクとして起動している時などに本体アプリと通信を行いたい時がある。
DistributedNotificationCenter を使用すればアプリ間通信が可能になる。

サンプルコードは以下に配置。
https://github.com/atsushijike/DistributedNotifications

  • Xcode10.1
  • Swift4.2

送信側

NotificationCenter と同じようにpostするのだが、 post(name:,object:)post(name:,object:,userInfo:) を使うと受信側アプリを前面にしないとobserveされなかったりするので、 postNotificationName(_ name:,object:,userInfo:,deliverImmediately:) を使用する。

userInfo には field.stringValue を"Message"キーで入れている。

PostApp/AppDelegate.swift
    @IBAction func post(_ sender: NSButton) {
        let userInfo: [AnyHashable : Any]? = field.stringValue.isEmpty ? nil : ["Message" : field.stringValue]
        DistributedNotificationCenter.default().postNotificationName(NSNotification.Name("DistributedNotifications.Post"), object: nil, userInfo: userInfo, deliverImmediately: true)
    }

但し、Sandbox対応アプリで userInfo を含むとエラーになるので注意
https://developer.apple.com/library/archive/documentation/Security/Conceptual/AppSandboxDesignGuide/DesigningYourSandbox/DesigningYourSandbox.html

With App Sandbox, you cannot include a userInfo dictionary when posting to an NSDistributedNotificationCenter object for messaging other tasks. 

受信側

NotificationCenter でobserveするときと同様に addObserver(_ observer:,selector:,name:,object:) する。

ObserverApp/AppDelegate.swift
    func applicationDidFinishLaunching(_ aNotification: Notification) {
        DistributedNotificationCenter.default().addObserver(self, selector: #selector(distributedNotificationsPost), name: NSNotification.Name("DistributedNotifications.Post"), object: nil)
    }

userInfo に含まれる"Message"を label に表示。

ObserverApp/AppDelegate.swift
    @objc private func distributedNotificationsPost(notification: Notification) {
        if let message = notification.userInfo?["Message"] as? String {
            label.stringValue = message
        }
    }