Send data Between iOS Apps and Extensions Using Darwin Notifications

Rizwan Ahmed
2 min readAug 28, 2024

--

In iOS development, app extensions run in separate processes from their containing apps. This separation poses a challenge when you need to communicate between the main app and its extensions. While NSNotificationCenter is a common choice for passing data between view controllers within the same app, it falls short when it comes to inter-process communication. Have you ever thought about how to pass data between the main app and its extension? Darwin notifications provide a powerful solution for this scenario. In this post, we’ll explore how to implement a Darwin Notifications manager and use it to facilitate real-time data transfer between a main app and its extension.

What are Darwin Notifications a.k.a CFNotificationCenterGetDarwinNotifyCenter?

CFNotificationCenterGetDarwinNotifyCenter is a function in Apple’s Core Foundation framework that provides access to the Darwin Notification Center. This notification center is designed for system-wide notifications, allowing different processes (such as your app and its extensions) to communicate with each other.

How Does it Work?

System-Wide Communication: Unlike NSNotificationCenter, which is limited to the app’s process, the Darwin Notification Center can send notifications that can be observed by other processes on the device. This makes it ideal for app-to-extension communication.

No UserInfo Dictionary: One limitation is that Darwin notifications do not support sending additional data (like a userInfo dictionary). This means you can only send a simple notification without any extra information. This is because the underlying mechanism, notify_post(), only accepts a string identifier for the notification

A use case for Darwin Notifications

For example, when a broadcast upload extension starts or stops, you can use a Darwin notification to inform the main app. I have seen most of the people use UserDefaults or the Keychain but I personally feel that Darwin notifications are the best fit for this use case.

Implementing the Darwin Notification Manager

To start, we’ll create a DarwinNotificationManager class that uses the CFNotificationCenter API to post and observe notifications across processes.

import Foundation

class DarwinNotificationManager {

static let shared = DarwinNotificationManager()

private init() {}


private var callbacks: [String: () -> Void] = [:]


func postNotification(name: String) {
let notificationCenter = CFNotificationCenterGetDarwinNotifyCenter()
CFNotificationCenterPostNotification(notificationCenter, CFNotificationName(name as CFString), nil, nil, true)
}


func startObserving(name: String, callback: @escaping () -> Void) {
callbacks[name] = callback

let notificationCenter = CFNotificationCenterGetDarwinNotifyCenter()

CFNotificationCenterAddObserver(notificationCenter,
Unmanaged.passUnretained(self).toOpaque(),
DarwinNotificationManager.notificationCallback,
name as CFString,
nil,
.deliverImmediately)
}


func stopObserving(name: String) {
let notificationCenter = CFNotificationCenterGetDarwinNotifyCenter()
CFNotificationCenterRemoveObserver(notificationCenter, Unmanaged.passUnretained(self).toOpaque(), CFNotificationName(name as CFString), nil)
callbacks.removeValue(forKey: name)
}


private static let notificationCallback: CFNotificationCallback = { center, observer, name, _, _ in
guard let observer = observer else { return }
let manager = Unmanaged<DarwinNotificationManager>.fromOpaque(observer).takeUnretainedValue()

if let name = name?.rawValue as String?, let callback = manager.callbacks[name] {
callback()
}
}
}

To understand more about Darwin Notifications, please refer to the original article: ‘Sending Data Between iOS Apps and Extensions Using Darwin Notifications’ available at this link: https://ohmyswift.com/blog/2024/08/27/send-data-between-ios-apps-and-extensions-using-darwin-notifications/

Thanks for reading!

--

--