Add Local Notification in iOS 10 - Swift 3 - swift

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

Related

Why my didRecive and willPresent aren’t working(action buttons do nothing)?

I want to make an actionable notification. It appears only if my app is closed. When I press action button nothing happening
My class of notification
class Notfication : NSObject, UNUserNotificationCenterDelegate {
func request(answer : rope, RopesBas : RopesBase){
let taskCategory = UNNotificationCategory(identifier: "task", actions: [UNNotificationAction(identifier: "done", title: "Done")], intentIdentifiers: [])
UNUserNotificationCenter.current().setNotificationCategories([taskCategory])
print("0 stage")
let content = UNMutableNotificationContent()
content.title = answer.name
content.sound =
UNNotificationSound.default
content.categoryIdentifier = "task"
content.userInfo=["name" : answer.name]
print("1 stage")
// show this notification five seconds from now
let trigger = UNTimeIntervalNotificationTrigger(timeInterval: 5, repeats: false)
print("2 stage")
//UNUserNotificationCenter.current().delegate = self
// choose a random identifier
let request = UNNotificationRequest(identifier: UUID().uuidString, content: content, trigger: trigger)
print("3 stage")
// add our notification request
UNUserNotificationCenter.current().add(request){ (error : Error?) in
if let theError = error {
print(theError.localizedDescription)
}
}
print("4 stage")
print(UNUserNotificationCenter.current().delegate)
print("ok")
}
func userNotificationCenter(_ center: UNUserNotificationCenter, didReceive response: UNNotificationResponse, withCompletionHandler completionHandler: #escaping () -> Void) {
print("5 stage")
let userInfo = response.notification.request.content.userInfo
let id = "dad"
print("6 stage")
print(response.actionIdentifier)
switch response.actionIdentifier {
case "done":
base.ropes.removeAll(where: {$0.name == id})
break
case UNNotificationDefaultActionIdentifier,
UNNotificationDismissActionIdentifier:
break
default :
break
}
print(base.ropes.count)
completionHandler()
}
func userNotificationCenter(_ center: UNUserNotificationCenter, willPresent notification: UNNotification, withCompletionHandler completionHandler: #escaping (UNNotificationPresentationOptions) -> Void) {
print("q")
completionHandler([.badge,.banner,.sound,.list])
}
}
I’m declaring this class at sheet view
struct Adding: View {
private let publisher = Notfication()
And using it when I press the button in a sheet view
Button(action: {
RopesDB.ropes.append(rope(name: answer.answer))
publisher.request(answer: RopesDB.ropes.last!, RopesBas: RopesDB)
dismiss()
}, label: {
HStack{
Spacer()
Text(answer.answer)
Spacer()
}
I’ve tried to put delegate into
class AppDelegate: NSObject, UIApplicationDelegate{
internal func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey : Any]? = nil) -> Bool {
let center = UNUserNotificationCenter.current()
let options : UNAuthorizationOptions = [.alert, .badge, .sound]
center.requestAuthorization(options: options) { granted, error in
if granted{
UNUserNotificationCenter.current().delegate = Notfication()
}
if let error = error {
print(error.localizedDescription)
}
}
return true
}
//
}
And into the class where it comment
What my mistake?
IDE Swiftplaygrounds(iPad 9, iOS 15.5)
Apple states in its documentation that notifications are only shown when the app is in the background. When the app already is in the foreground, the notification will be directly passed into your app:
https://developer.apple.com/documentation/usernotifications/handling_notifications_and_notification-related_actions
You will need to receive the notification in your delegate's userNotificationCenter(_:willPresent:withCompletionHandler:) method and there your app can decide what to actually do with it. E.g. throw the user to a certain view, show a modal etc.
I found out that this thing can’t work on Playgrounds, I suppose

Why aren't local notifications showing up on Mac?

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.

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()
}
}

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.

How to make a URL Run when a notification action is pressed?

I created a notification with two actions. One of my actions is called "Cancel", while the other is called "Call". How do I make the "Call" action run a URL that is in a comment that I added to my code. Here is my code:
func notificationFires(){
/**
URL Code:
if let url = URL(string: "tel://") {
UIApplication.shared.open(url, options: [:])
}
**/
let call = UNNotificationAction(identifier:"call", title:"Call", options:[.foreground])
let cancel = UNNotificationAction(identifier: "cancel", title: "Cancel", options: [.destructive ])
let category = UNNotificationCategory(identifier: "category", actions: [call, cancel], intentIdentifiers: [], options: [])
UNUserNotificationCenter.current().setNotificationCategories([category])
let notification = UILocalNotification()
notification.category = "category"
// 2
notification.soundName = UILocalNotificationDefaultSoundName
notification.fireDate = datePicker.date
// 3
if textField.text == "" {
notification.alertBody = "You have a call right now!"
}
else{
notification.alertBody = self.textField.text
}
// 4
notification.timeZone = NSTimeZone.default
// 5
// 6
notification.applicationIconBadgeNumber = 1
// 7
func application(application: UIApplication!, handleActionWithIdentifier identifier:String!, forLocalNotification notification:UILocalNotification!, completionHandler: (() -> Void)!){
if (identifier == "call"){
if let url = URL(string: "tel://2162964785") {
UIApplication.shared.open(url, options: [:])
}
}else if (identifier == "cancel"){
}
}
UIApplication.shared.scheduleLocalNotification(notification)
func application(application: UIApplication, didReceiveLocalNotification userInfo: [NSObject : AnyObject], fetchCompletionHandler completionHandler: (UIBackgroundFetchResult) -> Void) {
print("Recieved: notification")
let center = UNUserNotificationCenter.current()
center.removeDeliveredNotifications(withIdentifiers: ["notification"])
}
}
Assuming your notification is working correctly, you can conform to UNUserNotificationCenterDelegate to handle the "call" action.
Something like:
func userNotificationCenter(_ center: UNUserNotificationCenter, didReceive response: UNNotificationResponse, withCompletionHandler completionHandler: #escaping () -> Void) {
if response.actionIdentifier == "call" {
let body = response.notification.request.content.body
if let url = URL(string: "tel://\(body)") {
UIApplication.shared.open(url, options: [:], completionHandler: nil)
}
}
}
The body constant would be set to the phone number that you want to "open".
Also, its important to note that this has to be tested on an actual device. Opening a tel scheme does nothing in the simulator.
UNUserNotificationCenterDelegate API Reference: https://developer.apple.com/reference/usernotifications/unusernotificationcenterdelegate
EDIT:
You do not call the delegate method. Instead you implement it. The delegate method is called by the UNUserNotificationCenter.
To get this working, its important to ensure that you set the UNUserNotificationCenter.current() delegate property to a class that will conform to the UNUserNotificationCenterDelegate protocol.
For example, if you are handling your notification in your AppDelegate, you may have something like the following method:
func callNotification() {
let center = UNUserNotificationCenter.center()
// TODO: - Create your actions, category, content, trigger and request...
center.delegate = self // Important!
center.removeAllPendingNotificationRequests()
center.add(request, withCompletionHandler: nil)
}
The method above would be responsible for defining your notification and scheduling it. For brevity, I have left out all of the code that would define the notification since you indicated that this is working. Instead you should note that the delegate property is being set to self.
Then in an extension, you would make the AppDelegate conform to the UNUserNotificationCenterDelegate and implement the required methods.
extension AppDelegate: UNUserNotificationCenterDelegate {
func userNotificationCenter(_ center: UNUserNotificationCenter, didReceive response: UNNotificationResponse, withCompletionHandler completionHandler: #escaping () -> Void) {
if response.actionIdentifier == "call" {
let body = response.notification.request.content.body
if let url = URL(string: "tel://\(body)") {
UIApplication.shared.open(url, options: [:], completionHandler: nil)
}
}
}
}
Now because your AppDelegate conforms to the UNUserNotificationCenterDelegate protocol and you set self (AppDelegate) as the UNUserNotificationCenter's delegate, your implementation of the userNotification(_:didReceive:withCompletionHandler:) method will be called.