Why aren't local notifications showing up on Mac? - swift

I am trying to send local notifications from my Mac app(written in swift using Cocoa)
So far I've written this function
func scheduleNotification() {
let content = UNMutableNotificationContent()
content.title = "Test"
content.body = "This is a test"
content.sound = .default
content.badge = 1
let now = Date()
let int = timePicker.dateValue.timeIntervalSince(now)
let trigger = UNTimeIntervalNotificationTrigger(timeInterval: int, repeats: false)
let request = UNNotificationRequest(identifier: "Test notification", content: content, trigger: trigger)
center.add(request) { (err) in
if err == nil {
print("Success")
}
}
}
But for some reason it doesn't show a banner or anything in the Notification Center. Is something wrong with my code or something else causing this? (I checked and the app has the permission to send notifications)
And also, what is the equivalent on macOS for the
UIApplication.shared.applicationBadgeNumber = 0 on iOS?

If app is on foreground, you have to use UNUserNotificationCenterDelegate.
UNUserNotificationCenter.current().delegate = self
Firstly notify that delegate in viewDidLoad.
extension ViewController: UNUserNotificationCenterDelegate {
func userNotificationCenter(_ center: UNUserNotificationCenter, willPresent notification: UNNotification,
withCompletionHandler completionHandler: #escaping (UNNotificationPresentationOptions) -> Void) {
return completionHandler([.alert, .sound, .badge])
}
}
And after you can use delegate like that. Check this out more information this post -> Local Notifications

So, as it turns out asking for permission is mandatory in MacOS Catalina, that was all that missing.

Related

Can you disable App-opening mechanism when the user presses the Notification Banner?

Using iOS13.4, XCode11.4, Swift5.2,
In my current App, I successfully managed to create a Local Notification. A banner is shown when the notification occurs. The banner is shown independently of App-state (i.e. opened App running in Foreground or Background or even if the App is completely closed). So far so good :)
Now my question: Can you disable App-opening mechanism when the user presses the Notification Banner ?
Currently, I am using the UNUserNotificationCenterDelegate's Delegate methods to complete the notification (i.e. run some custom code when the user presses the banner). (see code-snippet below)
However, I do not want the App to necessarily open up for doing the completion work ! This could be perfectly done in the background for better customer experience.
Is it possible to run some code when the banner gets pressed but do this without App opening ? And if yes, how ?
Here is the code snippet:
import UIKit
class MyViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
let content = UNMutableNotificationContent()
content.title = "My Notification Title"
content.categoryIdentifier = UUID().uuidString
var notificationIdentifier = "23224-23134-234234"
var notificationDate = Date(timeInterval: 30, since: Date())
let notificationTriggerKind = Calendar.current.dateComponents([.day, .month, .year, .hour, .minute, .second], from: notificationDate)
let notificationTrigger = UNCalendarNotificationTrigger(dateMatching: notificationTriggerKind, repeats: false)
let notificationRequest = UNNotificationRequest(identifier: notificationIdentifier, content: content, trigger: notificationTrigger)
let notificationCenter = UNUserNotificationCenter.current()
notificationCenter.delegate = self
notificationCenter.add(notificationRequest) {(error) in if let error = error { print("error: \(error)") } }
}
}
extension MyViewController: UNUserNotificationCenterDelegate {
// kicks in when App is running in Foreground (without user interaction)
func userNotificationCenter(_ center: UNUserNotificationCenter,
willPresent notification: UNNotification,
withCompletionHandler completionHandler: #escaping (UNNotificationPresentationOptions) -> Void) {
// ...do your custom code...
completionHandler([.alert, .sound])
}
// kicks in when App is running in Foreground or Background
// (AND App is open) AND user interacts with notification
func userNotificationCenter(_ center: UNUserNotificationCenter,
didReceive response: UNNotificationResponse, withCompletionHandler
completionHandler: #escaping () -> Void) {
// ... do your custom code ....
return completionHandler()
}
}

Why is the local notification I created with UNUserNotificationCenter not shown in the upper-right corner of my screen?

Running the code below doesn't show the notification in the upper-right corner of my screen (as a banner or alert). The notification is shown in the notification center.
I made sure "Do not disturb" is disabled on my system. I also tried both the "Banner" and "Alert" setting in System Preferences -> Notifications -> Name of my app.
Source:
import Cocoa
import UserNotifications
#NSApplicationMain
class AppDelegate: NSObject, NSApplicationDelegate {
func applicationDidFinishLaunching(_ aNotification: Notification) {
let notificationCenter = UNUserNotificationCenter.current()
notificationCenter.requestAuthorization(options: [.alert, .badge, .sound]) { granted, error in
print("requestNotificationAuthorization: granted=\(granted) error=\(String(describing: error))")
}
let content = UNMutableNotificationContent()
content.title = "bar"
content.body = "foo"
content.categoryIdentifier = "alarm"
let uuidString = UUID().uuidString
let trigger = UNTimeIntervalNotificationTrigger(timeInterval: 2, repeats: false)
let request = UNNotificationRequest(identifier: uuidString, content: content, trigger: trigger)
notificationCenter.add(request, withCompletionHandler: { error in
print("completion handler called")
if error != nil {
print("notification center error: \(String(describing: error))")
}
})
}
func applicationWillTerminate(_ aNotification: Notification) {
}
}
Console output:
requestNotificationAuthorization: granted=true error=nil
completion handler called
if you want to see notification banner while app is in foreground use below method
func userNotificationCenter(_ center: UNUserNotificationCenter, willPresent notification: UNNotification,
withCompletionHandler completionHandler: #escaping (UNNotificationPresentationOptions) -> Void) {
// Forground notifications.
completionHandler([.alert, .sound])
}
Wanna see a magic trick?
If you are working with multiple displays, unplug them, and tadaa - notifications work.
I made sure "Do not disturb" is disabled on my system.
Go to Mac System Preferences->Notifications->Do not disturb (1st item on the list)
Turns out, by default, if you are mirroring your display, the "Do not disturb" mode gets enabled and you don't get any notifications. So, uncheck it, plug your other display back in and keep coding.
This is so stupid that it's almost funny.
The notification isn't shown when the app is running in the foreground. Relevant documentation.

UNUserNotificationCenter. Notifications are not allowed for this application

I noob in development application on OSX. I want to create app with Share extension. After content loading I want to show Notification, but I getting error "Notifications are not allowed for this application". I don't understand why requestAuthorization method not show dialog windows with permission, and how I can allow application to send notifications.
This is my code:
import Cocoa
import UserNotifications
class ShareViewController: NSViewController {
override func loadView() {
self.view = NSView()
// Insert code here to customize the view
let item = self.extensionContext!.inputItems[0] as! NSExtensionItem
NSLog("Attachment = %#", item.attachments! as NSArray)
showNotification()
let outputItem = NSExtensionItem()
let outputItems = [outputItem]
self.extensionContext!.completeRequest(returningItems: outputItems, completionHandler: nil)
}
func showNotification() -> Void {
let notificationCenter = UNUserNotificationCenter.current()
notificationCenter.requestAuthorization(options: [.alert, .badge]) {
(granted, error) in
if granted {
print("Yay!")
} else {
print("D'oh") // Print this, not authorized
}
}
let content = UNMutableNotificationContent()
content.title = "Hello"
content.body = "This is example"
content.sound = UNNotificationSound.default
content.badge = 1
let trigger = UNTimeIntervalNotificationTrigger(timeInterval: 5, repeats: false)
let request = UNNotificationRequest(identifier: UUID().uuidString, content: content, trigger: trigger)
notificationCenter.add(request) { (error : Error?) in
if let theError = error {
print(theError) // Print Domain=UNErrorDomain Code=1 "Notifications are not allowed for this application"
}
}
}
}
I don't see anywhere in the documentation that says you can't schedule new Local Notifications from an extension. I do however see an apple support ticket where someone had to deal with this issue, as do I.
Basically this failure is a race-condition. The key is to not call the contentHandler of this extension method:
override func didReceive(_ request: UNNotificationRequest, withContentHandler contentHandler: #escaping (UNNotificationContent) -> Void) {
until after the completion handler of
notificationCenter.add(request: UNNotificationRequest>, withCompletionHandler: ((Error?) -> Void)
is called. It worked for me. Make sense?
Apple does't allow send Notifications from Share extensions.
Documentation

SWIFT send local notification

i'm trying to send a local notification following this tutorial:
https://useyourloaf.com/blog/local-notifications-with-ios-10/
I added this code to my viewDidLoad():
let center = UNUserNotificationCenter.current()
let options: UNAuthorizationOptions = [.alert, .sound];
center.requestAuthorization(options: options) {
(granted, error) in
if !granted {
print("Something went wrong")
}
}
let content = UNMutableNotificationContent()
content.title = "Don't forget"
content.body = "Buy some milk"
content.sound = UNNotificationSound.default()
let trigger = UNTimeIntervalNotificationTrigger(timeInterval: 5,
repeats: false)
let identifier = "UYLLocalNotification"
let request = UNNotificationRequest(identifier: identifier,
content: content, trigger: trigger)
center.add(request, withCompletionHandler: { (error) in
if let error = error {
print("error")
}
})
but 5 seconds pass without any notification shown. the CompletionHandler is called with error being nil
If you want the notification when app is in foreground, then you'll have to add some more code into your controller,
Add the following line at the end of viewDidLoad()
center.delegate = self
This makes your ViewController the delegate to which the notification can callback when it hits.
Confirm your view controller to the delegate, (Add UNUserNotificationCenterDelegate)
class ViewController: UIViewController, UNUserNotificationCenterDelegate {
Now you can write callback methods in your ViewController.
Write the callback to present your notification, you can add this method right after viewDidLoad()
func userNotificationCenter(_ center: UNUserNotificationCenter,
willPresent notification: UNNotification,
withCompletionHandler completionHandler: #escaping (UNNotificationPresentationOptions) -> Void) {
completionHandler([.alert, .badge, .sound])
}
This will make your notification to popup even if the app is in foreground.
NOTE: It is highly recommended that you register your notifications (Local/Remote) in AppDelegate rather than ViewController.

Add Local Notification in iOS 10 - Swift 3

So I been trying to add a notification to the new UNUserNotificationCenter, but I don't seem to get it.
My view controller has an action:
#IBAction func sendPressed(_ sender: AnyObject) {
let content = UNMutableNotificationContent()
content.title = "Hello"
content.body = "What up?"
content.sound = UNNotificationSound.default()
// Deliver the notification in five seconds.
let trigger = UNTimeIntervalNotificationTrigger.init(timeInterval: 5, repeats: false)
let request = UNNotificationRequest.init(identifier: "FiveSecond", content: content, trigger: trigger)
// Schedule the notification.
let center = UNUserNotificationCenter.current()
center.add(request) { (error) in
print(error)
}
print("should have been added")
}
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
let center = UNUserNotificationCenter.current()
center.requestAuthorization([.alert, .sound]) { (granted, error) in
}
}
And I have a Notification Content Extension in the project as well, but it does not seem to be triggered at all, any ideas what I'm missing? I'm trying the example from the user documentation, but it's not telling me much more or I have missed it.
Here: https://developer.apple.com/reference/usernotifications/unmutablenotificationcontent
Also:
https://developer.apple.com/reference/usernotificationsui
https://developer.apple.com/reference/usernotifications
Edit:
So putting the app in the background did the trick.
You need to register for Notification...I tried and this works.
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
// Override point for customization after application launch.
let center = UNUserNotificationCenter.current()
center.requestAuthorization([.alert, .sound]) { (granted, error) in
// Enable or disable features based on authorization.
}
return true
}
Edit: You dont need to put your app in background to present notification from iOS 10 onwards.
Use below callback to configure notification to present in foreground.
func userNotificationCenter(_ center: UNUserNotificationCenter, willPresent notification: UNNotification, withCompletionHandler completionHandler: #escaping (UNNotificationPresentationOptions) -> Void)
Here is a sample project.
With Objective-C implemation:
I have wrote a Demo project here: iOS10AdaptationTips .
import UserNotifications
///Notification become independent from Foundation
#import UserNotifications;
request authorization for localNotification
UNUserNotificationCenter *center = [UNUserNotificationCenter currentNotificationCenter];
[center requestAuthorizationWithOptions:(UNAuthorizationOptionBadge | UNAuthorizationOptionSound | UNAuthorizationOptionAlert)
completionHandler:^(BOOL granted, NSError * _Nullable error) {
if (!error) {
NSLog(#"request authorization succeeded!");
[self showAlert];
}
}];
Request Authorization:
schedule localNotification
update application icon badge number
// //Deliver the notification at 08:30 everyday
// NSDateComponents *dateComponents = [[NSDateComponents alloc] init];
// dateComponents.hour = 8;
// dateComponents.minute = 30;
// UNCalendarNotificationTrigger *trigger = [UNCalendarNotificationTrigger triggerWithDateMatchingComponents:dateComponents repeats:YES];
UNMutableNotificationContent *content = [[UNMutableNotificationContent alloc] init];
content.title = [NSString localizedUserNotificationStringForKey:#"Elon said:" arguments:nil];
content.body = [NSString localizedUserNotificationStringForKey:#"Hello Tom!Get up, let's play with Jerry!"
arguments:nil];
content.sound = [UNNotificationSound defaultSound];
/// 4. update application icon badge number
content.badge = #([[UIApplication sharedApplication] applicationIconBadgeNumber] + 1);
// Deliver the notification in five seconds.
UNTimeIntervalNotificationTrigger *trigger = [UNTimeIntervalNotificationTrigger
triggerWithTimeInterval:5.f repeats:NO];
UNNotificationRequest *request = [UNNotificationRequest requestWithIdentifier:#"FiveSecond"
content:content trigger:trigger];
/// 3. schedule localNotification
UNUserNotificationCenter *center = [UNUserNotificationCenter currentNotificationCenter];
[center addNotificationRequest:request withCompletionHandler:^(NSError * _Nullable error) {
if (!error) {
NSLog(#"add NotificationRequest succeeded!");
}
}];
then it will appear like this:
In Background :
Lock Screen:
If Repeat by default only show one
instead of show many on the lock screen on iOS9:
and also support 3D Touch automatically
I write a Demo here: iOS10AdaptationTips .
I solved my problem as follows (Firebase, Swift 3):
Find this method on your AppDelegate:
func userNotificationCenter(_ center: UNUserNotificationCenter, willPresent notification: UNNotification, withCompletionHandler completionHandler: #escaping (UNNotificationPresentationOptions) -> Void) {
Find this line:
completionHandler()
End set:
completionHandler([.alert,.sound,.badge])
notifications are not firing if you not pass your presentation options to completionHandler method.
Here are a few steps:
Make sure you have the permission. If not, use UNUserNotificationCenter.current().requestAuthorization to get that. Or follow the answer if you want to show the request pop up more than once.
If you want to show the notification foreground, having to assign UNUserNotificationCenterDelegate to somewhere.
Show me the code
#IBAction func sendPressed(_ sender: AnyObject) {
let content = UNMutableNotificationContent()
content.title = "Hello"
content.body = "What up?"
content.sound = UNNotificationSound.default()
let trigger = UNTimeIntervalNotificationTrigger.init(timeInterval: 5, repeats: false)
let request = UNNotificationRequest.init(identifier: "FiveSecond", content: content, trigger: trigger)
let center = UNUserNotificationCenter.current()
center.add(request) { (error) in
print(error)
}
}
override func viewDidLoad(_ animated: Bool) {
super.viewDidLoad(animated)
// Assign the delegate
UNUserNotificationCenter.current().delegate = self
// Ask the permission
let center = UNUserNotificationCenter.current()
center.requestAuthorization([.alert, .sound]) { (granted, error) in
if granted {
// do something
}
}
}
// Remember to add UNUserNotificationCenterDelegate to your view controller
func userNotificationCenter(_ center: UNUserNotificationCenter, willPresent notification: UNNotification, withCompletionHandler completionHandler: #escaping (UNNotificationPresentationOptions) -> Void) {
print("Got the msg...")
completionHandler([.badge, .sound, .alert])
}
I have made an implementation for Swift 3 which may help, you can check it here: https://stackoverflow.com/a/45381380/2296630