Reputation: 1340
I'm working on an application which uses a shared Core Data database between itself and a Notification Service Extension. Both the application and the extension are able to read and write to the same Core Data database.
The application however needs to update the displayed information as soon as the corresponding fields change in the database. Is there an efficient way for it to be notified of the changes the extension makes to the database? I assume the application and the extension use different managed contexts to access the database. Or am I wrong?
Upvotes: 2
Views: 566
Reputation: 6722
Using SwiftEventBus
this is pretty straight forward
Controller.swift
let yourObj = YourObject()
SwiftEventBus.post("EventName", sender: yourObj)
Extension.swift
let yourObj = YourObject()
SwiftEventBus.post("EventName", sender: yourObj)
AppDelegate.swift
SwiftEventBus.onMainThread(self, name: "EventName") { (result) in
if let yourObject = result.object as? YourObject {
// Queue or write the data as per your need
}
}
Upvotes: 1
Reputation: 1340
I found a solution to the problem I described after being pointed towards using notification by @CerlinBoss. It is possible to send a notification from the extension to the application (or vice versa). This can be done in iOS using a Darwin notification center. The limitation however is that you can't use the notification to send custom data to your application.
After reading many articles I decided that I'd avoid making changes to the Core Data database from two different processes and using multiple managed contexts. Instead, I queue the data I need to communicate to the application inside a key in the UserDefaults and once the application is notified of the changes, I'd dequeue them and update the Core Data context.
import os
import Foundation
open class UserDefaultsManager {
// MARK: - Properties
static let applicationGroupName = "group.com.organization.Application"
// MARK: - Alert Queue Functions
public static func queue(notification: [AnyHashable : Any]) {
guard let userDefaults = UserDefaults(suiteName: applicationGroupName) else {
return
}
// Retrieve the already queued notifications.
var alerts = [[AnyHashable : Any]]()
if let data = userDefaults.data(forKey: "Notifications"),
let items = NSKeyedUnarchiver.unarchiveObject(with: data) as? [[AnyHashable : Any]] {
alerts.append(contentsOf: items)
}
// Add the new notification to the queue.
alerts.append(notification)
// Re-archive the new queue.
let data = NSKeyedArchiver.archivedData(withRootObject: alerts)
userDefaults.set(data, forKey: "Notifications")
}
public static func dequeue() -> [[AnyHashable : Any]] {
var notifications = [[AnyHashable : Any]]()
// Retrieve the queued notifications.
if let userDefaults = UserDefaults(suiteName: applicationGroupName),
let data = userDefaults.data(forKey: "Notifications"),
let items = NSKeyedUnarchiver.unarchiveObject(with: data) as? [[AnyHashable : Any]] {
notifications.append(contentsOf: items)
// Remove the dequeued notifications from the archive.
userDefaults.removeObject(forKey: "Notifications")
}
return notifications
}
}
override func didReceive(_ request: UNNotificationRequest, withContentHandler contentHandler: @escaping (UNNotificationContent) -> Void) {
self.contentHandler = contentHandler
bestAttemptContent = (request.content.mutableCopy() as? UNMutableNotificationContent)
if let bestAttemptContent = bestAttemptContent {
os_log("New notification received! [%{public}@]", bestAttemptContent.body)
// Modify the notification content here...
// Queue the notification and notify the application to process it
UserDefaultsManager.queue(notification: bestAttemptContent.userInfo)
notifyApplication()
contentHandler(bestAttemptContent)
}
}
func notifyApplication() {
let name: CFNotificationName = CFNotificationName.init("mutableNotificationReceived" as CFString)
if let center = CFNotificationCenterGetDarwinNotifyCenter() {
CFNotificationCenterPostNotification(center, name, nil, nil, true)
os_log("Application notified!")
}
}
// Subscribe to the mutableNotificationReceived notifications from the extension.
if let center = CFNotificationCenterGetDarwinNotifyCenter() {
let name = "mutableNotificationReceived" as CFString
let suspensionBehavior = CFNotificationSuspensionBehavior.deliverImmediately
CFNotificationCenterAddObserver(center, nil, mutableNotificationReceivedCallback, name, nil, suspensionBehavior)
}
let mutableNotificationReceivedCallback: CFNotificationCallback = { center, observer, name, object, userInfo in
let notifications = UserDefaultsManager.dequeue()
for notification in notifications {
// Update your Core Data contexts from here...
}
print("Processed \(notifications.count) dequeued notifications.")
}
Upvotes: 0