Notification Center very seldom returns some error from the completion handler. However, there is one error I encountered and it was pretty
mysterious, it was saying: “Notifications are not allowed for this application“.
This error message doesn’t say much. Of course, I had permission to show notification, so what was wrong?
If you have this error in a different place than Notification Service Extension, it might not help you, but if it does let me know, I’ll update this post.
This error appears in Notification Service Extension when you try to schedule notification after content handler.
Let’s say you have simple notification scheduling inside the service.
Look at this code:
var contentHandler: ((UNNotificationContent) -> Void)?
var bestAttemptContent: UNMutableNotificationContent?
override func didReceive(_ request: UNNotificationRequest, withContentHandler contentHandler: @escaping (UNNotificationContent) -> Void) {
self.contentHandler = contentHandler
bestAttemptContent = (request.content.mutableCopy() as? UNMutableNotificationContent)
print(“Start”)
if let bestAttemptContent = bestAttemptContent {
print(“Finish”)
contentHandler(bestAttemptContent)
}
let content = UNMutableNotificationContent()
content.title = “Notification title”
let trigger = UNTimeIntervalNotificationTrigger(timeInterval: 120, repeats: false)
let request = UNNotificationRequest(identifier: “identifier”, content: content, trigger: trigger)
UNUserNotificationCenter.current().add(request, withCompletionHandler: { error in
print(“Error: \(String(describing: error))“)
})
}
When you run it, every time when service tries to create a notification, the error will occur.
The solution is simple, however, if you have complex logic inside the service it might be more difficult for you. To solve it you need to execute the content handler after requesting notification.
I think the best solution is executing the content handler inside the completion handler of the notification center.
override func didReceive(_ request: UNNotificationRequest, withContentHandler contentHandler: @escaping (UNNotificationContent) -> Void) {
self.contentHandler = contentHandler
bestAttemptContent = (request.content.mutableCopy() as? UNMutableNotificationContent)
print(“Start”)
let content = UNMutableNotificationContent()
content.title = “Notification title”
let trigger = UNTimeIntervalNotificationTrigger(timeInterval: 120, repeats: false)
let request = UNNotificationRequest(identifier: “identifier”, content: content, trigger: trigger)
UNUserNotificationCenter.current().add(request, withCompletionHandler: { error in
print(“Error: \(String(describing: error))“)
if let bestAttemptContent = self.bestAttemptContent {
print(“Finish”)
contentHandler(bestAttemptContent)
}
})
}
You can also use DispatchSemaphore.
override func didReceive(_ request: UNNotificationRequest, withContentHandler contentHandler: @escaping
(UNNotificationContent) -> Void) {
self.contentHandler = contentHandler
bestAttemptContent = (request.content.mutableCopy() as? UNMutableNotificationContent)
print(“Start”)
let sempahore = DispatchSemaphore(value: 0)
let content = UNMutableNotificationContent()
content.title = “Notification title”
let trigger = UNTimeIntervalNotificationTrigger(timeInterval: 120, repeats: false)
let request = UNNotificationRequest(identifier: “identifier”, content: content, trigger: trigger)
UNUserNotificationCenter.current().add(request, withCompletionHandler: { error in
print(“Error: \(String(describing: error))“)
sempahore.signal()
})
sempahore.wait()
if let bestAttemptContent = self.bestAttemptContent {
print(“Finish”)
contentHandler(bestAttemptContent)
}
}
Another option is to use for example sleep thread.
override func didReceive(_ request: UNNotificationRequest, withContentHandler contentHandler: @escaping (UNNotificationContent) -> Void) {
self.contentHandler = contentHandler
bestAttemptContent = (request.content.mutableCopy() as? UNMutableNotificationContent)
print(“Start”)
let content = UNMutableNotificationContent()
content.title = “Notification title”
let trigger = UNTimeIntervalNotificationTrigger(timeInterval: 120, repeats: false)
let request = UNNotificationRequest(identifier: “identifier”, content: content, trigger: trigger)
UNUserNotificationCenter.current().add(request, withCompletionHandler: { error in
print(“Error: \(String(describing: error))“)
})
sleep(1)
if let bestAttemptContent = bestAttemptContent {
print(“Finish”)
contentHandler(bestAttemptContent)
}
}
I guess you can find more solutions but I want to give some examples.
The best option is the first solution.