Local notification does not show custom action button - ios

To test local notifications, I wrote a test app with a single view controller.
In viewDidLoad, I set up the custom action, the notification category, and the userNotificationCenter delegate.
In viewDidAppear, I set the notification content, setup a trigger that fires after 5 sec, create the notification request, and add it to the notification center.
I expect the following:
Foreground mode:
When the app is launched, it should present after 5 sec the notification in foreground. Before, the delegate function „willPresent notification“ should be called.
Background mode:
If, however, the app is put into background by pressing the home button before the trigger fires, the notification should be presented in the home screen, and the delegate function „willPresent notification“ is not called.
After the notification has been presented, the user can tap the action button.
This should bring the app into foreground, and trigger the „didReceive response“ delegate function.
What happens is:
The action button in never shown, only title and body.
When I tap the body, the delegate function „didReceive response“ is triggered using the default action identifier.
The problem:
Why is the custom action button not shown?
Here is my code:
import UIKit
import UserNotifications
class ViewController: UIViewController, UNUserNotificationCenterDelegate {
let userNotificationCenter = UNUserNotificationCenter.current()
let categotyId = "categoryID"
let actionID = "actionID"
override func viewDidLoad() {
super.viewDidLoad()
userNotificationCenter.requestAuthorization(options: [.alert]) { (granted, error) in
if granted {
let okAction = UNNotificationAction(identifier: self.actionID,
title: "OK",
options: [])
let category = UNNotificationCategory(identifier: self.categotyId,
actions: [okAction],
intentIdentifiers: [],
options: [.customDismissAction])
self.userNotificationCenter.setNotificationCategories([category])
self.userNotificationCenter.delegate = self
} else {
print("local notifications not granted")
}
}
userNotificationCenter.removeAllPendingNotificationRequests()
}
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
let content = UNMutableNotificationContent()
content.title = NSString.localizedUserNotificationString(forKey: "Title", arguments: nil)
content.body = NSString.localizedUserNotificationString(forKey: "Body", arguments: nil)
content.categoryIdentifier = categotyId
let trigger = UNTimeIntervalNotificationTrigger(timeInterval: (5), repeats: false)
let request = UNNotificationRequest.init(identifier: "requestID",
content: content,
trigger: trigger)
userNotificationCenter.add(request, withCompletionHandler: { (error) in
if let error = error {
print("Could not add notification request. Error: \(error)")
}
})
}
// MARK: - Notification Delegate
// Will be called while app is in the foreground
func userNotificationCenter(_ center: UNUserNotificationCenter,
willPresent notification: UNNotification,
withCompletionHandler completionHandler: #escaping (UNNotificationPresentationOptions) -> Void) {
// Show alert to the user
print("App in foreground. Show alert.")
completionHandler([.alert])
}
// Should be called after the user tapped the action button
func userNotificationCenter(_ center: UNUserNotificationCenter,
didReceive response: UNNotificationResponse,
withCompletionHandler completionHandler: #escaping () -> Void) {
let request = response.notification.request
let requestID = request.identifier
switch response.actionIdentifier {
case actionID:
print("Custom OK action triggered in background")
case UNNotificationDefaultActionIdentifier:
print("Default action triggered in background")
default:
print("Unknown action triggered in background, action identifier: \(response.actionIdentifier)")
}
UNUserNotificationCenter.current().removePendingNotificationRequests(withIdentifiers: [requestID])
completionHandler()
}
}

Sorry for my question, but maybe somebody else has the same problem:
I simply did not know that first, only title/body is displayed:
However, I was not aware of the thin grey bar below the body. If this bar is pulled down, the custom action button appears:

Update: As of iOS 10 beta 2, rich notifications are also available on pre-3D touch devices. Pull down on the regular notification to see it.
Make sure you are testing on a iPhone6s/iPhone6s plus simulator/device, it doesn't seem to work on pre-3D touch devices.
On a iPhone6 simulator, try to click and drag down on the stock notification you get and you should see your custom UI appear.

Related

Show Hyper Link in remote notification with Message

I need to show hyperlink in my remote notification along with Title and body. I have done some thing like this:
#IBAction func openPDFButtonPressed(_ sender: Any) {
self.scheduleNotification()
}
func scheduleNotification() {
let center = UNUserNotificationCenter.current()
let content = UNMutableNotificationContent()
content.title = "Download The Receipt"
content.body = "Kindly Download your purchase bill"
content.categoryIdentifier = "PDF"
content.userInfo = ["customData": "http://www.pdf995.com/samples/pdf.pdf"]
content.sound = UNNotificationSound.default
var dateComponents = DateComponents()
dateComponents.hour = 10
dateComponents.minute = 30
let trigger = UNTimeIntervalNotificationTrigger(timeInterval: 5, repeats: false)
let request = UNNotificationRequest(identifier: UUID().uuidString, content: content, trigger: trigger)
center.add(request)
}
func userNotificationCenter(_ center: UNUserNotificationCenter, didReceive response: UNNotificationResponse, withCompletionHandler completionHandler: #escaping () -> Void) {
// pull out the buried userInfo dictionary
let userInfo = response.notification.request.content.userInfo
print("Test: \(userInfo)")
if let customData = userInfo["customData"] as? String {
print("Custom data received: \(customData)")
let storyBoard: UIStoryboard = UIStoryboard(name: "Main", bundle: nil)
let newViewController = storyBoard.instantiateViewController(withIdentifier: "PDFViewController") as! PDFViewController
newViewController.strURL = customData
self.present(newViewController, animated: true, completion: nil)
}
completionHandler()
}
In this I am sending url in user info but I need this url as hyperlink that shows when notification appears. and when I click on that hyperlink it will open this URL in webView.
Loading URL in webview part is done. Just need to know how can I show this url as hyperlink on notification.
kindly help me out.
You cannot control the way a notification is shown by iOS, but you can declare specific actions for your notifications. See here: https://developer.apple.com/documentation/usernotifications/declaring_your_actionable_notification_types
This lets you add your custom 'Pay, Reject, Block' actions to the notification. iOS will offer the choices to the user, and notify your app in the background when the user selects one, but you will not be able to show the URL, only text.
The only way to show a custom dialog following a notification is if you get the notification while the app is in the foreground, because then the OS doesn't show the notification, it only notifies your app, and you can then decide to show whatever UI suits you. But that kind of goes against the idea of the notification, which can come in at any time, in particular when your app is not running.

Not able to perform action on push notification custom buttons

I am working on an iOS application which involves device to device push notification. In Foreground and Background state, I am able to receive notification and able to perform respective actions in respective custom buttons (ACCEPT & REJECT). Everything works fine in the two mentioned states. But in killed/terminated state, although I am able to receive notification, but I am not able to perform action on clicking custom buttons (ACCEPT & REJECT). Can you guys help me this?
//Notification action button function
func setActionCategories(){
let acceptAction = UNNotificationAction(
identifier: NAString().notificationAcceptIdentifier(),
title: NAString().accept().capitalized,
options: [.init(rawValue: 0)])
let rejectAction = UNNotificationAction(
identifier: NAString().notificationRejectIdentifier(),
title: NAString().reject().capitalized,
options: [.init(rawValue: 0)])
let actionCategory = UNNotificationCategory(
identifier: NAString().notificationActionCategory(),
actions: [acceptAction,rejectAction],
intentIdentifiers: [],
options: [.customDismissAction])
UNUserNotificationCenter.current().setNotificationCategories(
[actionCategory])
}
func userNotificationCenter(_ center: UNUserNotificationCenter, didReceive response: UNNotificationResponse, withCompletionHandler completionHandler: #escaping () -> Void) {
let userInfo = response.notification.request.content.userInfo
//Here we are performing Action on Notification Buttons & We created this buttons in "setActionCategories" function.
if response.notification.request.content.categoryIdentifier == NAString().notificationActionCategory() {
//Created Firebase reference to get currently invited visitor by E-Intercom
var gateNotificationRef : DatabaseReference?
gateNotificationRef = GlobalUserData.shared.getUserDataReference().child(Constants.FIREBASE_CHILD_GATE_NOTIFICATION).child(userUID).child(guestType!).child(guestUID!)
//Performing accept & reject on click of recently invited visitor by E-Intercom from Notification view.
switch response.actionIdentifier {
//If Accept button will pressed
case NAString().notificationAcceptIdentifier():
gateNotificationRef?.child(NAString().status()).setValue(NAString().accepted())
}
break
//If Reject button will pressed
case NAString().notificationRejectIdentifier(): gateNotificationRef?.child(NAString().status()).setValue(NAString().rejected())
break
default:
break
}
}
UIApplication.shared.applicationIconBadgeNumber = 0
completionHandler()
}
Hi Ashish can you provide some code for us to better assist you. There should be a completion handler in there where you can add an action function. Then you can perform whatever you need the buttons to do.
Add the below condition in didFinishLaunchingWithOptions delegate
if launchOptions?[UIApplicationLaunchOptionsKey.remoteNotification] != nil {
// Do what you want to do when remote notification is tapped.
}
you can call didReceive delegate method in it.

Usernotification framework badge does not increase

I am using UserNotification framework in my app and sending local notifications (not push notifications), and I want to set the badge to the number of notifications received so what I did was to set the number of notifications received into a user default then I tried to assign the value to the badge to get me a badge number but the badge number would not increase. This is my code below
To set value of received notification
center.getDeliveredNotifications { notification in
UserDefaults.standard.set(notification.count, forKey: Constants.NOTIFICATION_COUNT)
print("notification.count \(notification.count)")
print(".count noti \(UserDefaults.standard.integer(forKey: Constants.NOTIFICATION_COUNT))")
}
This accurately prints the number of notification received and when I decided to set it to my badge it only shows 1
content.badge = NSNumber(value: UserDefaults.standard.integer(forKey: Constants.NOTIFICATION_COUNT))
I have no idea why the value does not increase every time. Any help would be appreciated.
Or if it is possible to always update the badge anywhere in the app.
Send the local notifications like so:
func sendNotification(title: String, subtitle: String, body: String, timeInterval: TimeInterval) {
let center = UNUserNotificationCenter.current()
center.getPendingNotificationRequests(completionHandler: { pendingNotificationRequests in
//Use the main thread since we want to access UIApplication.shared.applicationIconBadgeNumber
DispatchQueue.main.sync {
//Create the new content
let content = UNMutableNotificationContent()
content.title = title
content.subtitle = subtitle
content.body = body
//Let's store the firing date of this notification in content.userInfo
let firingDate = Date().timeIntervalSince1970 + timeInterval
content.userInfo = ["timeInterval": firingDate]
//get the count of pending notification that will be fired earlier than this one
let earlierNotificationsCount: Int = pendingNotificationRequests.filter { request in
let userInfo = request.content.userInfo
if let time = userInfo["timeInterval"] as? Double {
if time < firingDate {
return true
} else {
//Here we update the notofication that have been created earlier, BUT have a later firing date
let newContent: UNMutableNotificationContent = request.content.mutableCopy() as! UNMutableNotificationContent
newContent.badge = (Int(truncating: request.content.badge ?? 0) + 1) as NSNumber
let newRequest: UNNotificationRequest =
UNNotificationRequest(identifier: request.identifier,
content: newContent,
trigger: request.trigger)
center.add(newRequest, withCompletionHandler: { (error) in
// Handle error
})
return false
}
}
return false
}.count
//Set the badge
content.badge = NSNumber(integerLiteral: UIApplication.shared.applicationIconBadgeNumber + earlierNotificationsCount + 1)
let trigger = UNTimeIntervalNotificationTrigger(timeInterval: timeInterval,
repeats: false)
let requestIdentifier = UUID().uuidString //You probably want to save these request identifiers if you want to remove the corresponding notifications later
let request = UNNotificationRequest(identifier: requestIdentifier,
content: content, trigger: trigger)
center.add(request, withCompletionHandler: { (error) in
// Handle error
})
}
})
}
(You may need to save the requests' identifiers (either in user defaults or core data if you'd like to update them, or even cancel them via removePendingNotificationRequests(withIdentifiers:))
You can call the above function like so:
sendNotification(title: "Meeting Reminder",
subtitle: "Staff Meeting in 20 minutes",
body: "Don't forget to bring coffee.",
timeInterval: 10)
Declare your view controller as a UNUserNotificationCenterDelegate:
class ViewController: UIViewController, UNUserNotificationCenterDelegate {
override func viewDidLoad() {
super.viewDidLoad()
UNUserNotificationCenter.current().delegate = self
}
//...
}
And to handle interacting with the notification, update the badge of the app, and the badge of the upcoming notifications:
func userNotificationCenter(_ center: UNUserNotificationCenter, didReceive response: UNNotificationResponse, withCompletionHandler completionHandler: #escaping () -> Void) {
//UI updates are done in the main thread
DispatchQueue.main.async {
UIApplication.shared.applicationIconBadgeNumber -= 1
}
let center = UNUserNotificationCenter.current()
center.getPendingNotificationRequests(completionHandler: {requests in
//Update only the notifications that have userInfo["timeInterval"] set
let newRequests: [UNNotificationRequest] =
requests
.filter{ rq in
return rq.content.userInfo["timeInterval"] is Double?
}
.map { request in
let newContent: UNMutableNotificationContent = request.content.mutableCopy() as! UNMutableNotificationContent
newContent.badge = (Int(truncating: request.content.badge ?? 0) - 1) as NSNumber
let newRequest: UNNotificationRequest =
UNNotificationRequest(identifier: request.identifier,
content: newContent,
trigger: request.trigger)
return newRequest
}
newRequests.forEach { center.add($0, withCompletionHandler: { (error) in
// Handle error
})
}
})
completionHandler()
}
This updates the app badge by decreasing it when a notification is interacted with ie tapped. Plus it updates the content badge of the pending notifications. Adding a notification request with the same identifier just updates the pending notification.
To receive notifications in the foreground, and increase the app badge icon if the notification is not interacted with, implement this:
func userNotificationCenter(_ center: UNUserNotificationCenter, willPresent notification: UNNotification, withCompletionHandler completionHandler: #escaping (UNNotificationPresentationOptions) -> Void) {
DispatchQueue.main.async {
UIApplication.shared.applicationIconBadgeNumber += 1
}
completionHandler([.alert, .sound])
}
Here are some gifs:
1st: Receiving local notifications increases the app badge. Whereas interacting with a notification decreases the app badge.
2nd: Receiving local notifications when the app is killed (I used a trigger timeInterval of 15s in this).
3rd: Receiving notification whilst in the foreground increases the app badge unless the user interacts with it.
The complete class used in my test project looks like this:
import UIKit
import UserNotifications
class ViewController: UIViewController, UNUserNotificationCenterDelegate {
var bit = true
#IBAction func send(_ sender: UIButton) {
let time: TimeInterval = bit ? 8 : 4
bit.toggle()
sendNotification(title: "Meeting Reminder",
subtitle: "Staff Meeting in 20 minutes",
body: "Don't forget to bring coffee.",
timeInterval: time)
}
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
UNUserNotificationCenter.current().delegate = self
}
func sendNotification(title: String, subtitle: String, body: String, timeInterval: TimeInterval) {
let center = UNUserNotificationCenter.current()
center.getPendingNotificationRequests(completionHandler: { pendingNotificationRequests in
DispatchQueue.main.sync {
let content = UNMutableNotificationContent()
content.title = title
content.subtitle = subtitle
content.body = body
let firingDate = Date().timeIntervalSince1970 + timeInterval
content.userInfo = ["timeInterval": firingDate]
let earlierNotificationsCount: Int = pendingNotificationRequests.filter { request in
let userInfo = request.content.userInfo
if let time = userInfo["timeInterval"] as? Double {
if time < firingDate {
return true
} else {
let newContent: UNMutableNotificationContent = request.content.mutableCopy() as! UNMutableNotificationContent
newContent.badge = (Int(truncating: request.content.badge ?? 0) + 1) as NSNumber
let newRequest: UNNotificationRequest =
UNNotificationRequest(identifier: request.identifier,
content: newContent,
trigger: request.trigger)
center.add(newRequest, withCompletionHandler: { (error) in
// Handle error
})
return false
}
}
return false
}.count
content.badge = NSNumber(integerLiteral: UIApplication.shared.applicationIconBadgeNumber + earlierNotificationsCount + 1)
let trigger = UNTimeIntervalNotificationTrigger(timeInterval: timeInterval,
repeats: false)
let requestIdentifier = UUID().uuidString //You probably want to save these request identifiers if you want to remove the corresponding notifications later
let request = UNNotificationRequest(identifier: requestIdentifier,
content: content, trigger: trigger)
center.add(request, withCompletionHandler: { (error) in
// Handle error
})
}
})
}
func userNotificationCenter(_ center: UNUserNotificationCenter, willPresent notification: UNNotification, withCompletionHandler completionHandler: #escaping (UNNotificationPresentationOptions) -> Void) {
DispatchQueue.main.async {
UIApplication.shared.applicationIconBadgeNumber += 1
}
completionHandler([.alert, .sound])
}
func userNotificationCenter(_ center: UNUserNotificationCenter, didReceive response: UNNotificationResponse, withCompletionHandler completionHandler: #escaping () -> Void) {
DispatchQueue.main.async {
UIApplication.shared.applicationIconBadgeNumber -= 1
}
let center = UNUserNotificationCenter.current()
center.getPendingNotificationRequests(completionHandler: {requests in
let newRequests: [UNNotificationRequest] =
requests
.filter{ rq in
return rq.content.userInfo["timeInterval"] is Double?
}
.map { request in
let newContent: UNMutableNotificationContent = request.content.mutableCopy() as! UNMutableNotificationContent
newContent.badge = (Int(truncating: request.content.badge ?? 0) - 1) as NSNumber
let newRequest: UNNotificationRequest =
UNNotificationRequest(identifier: request.identifier,
content: newContent,
trigger: request.trigger)
return newRequest
}
newRequests.forEach { center.add($0, withCompletionHandler: { (error) in
// Handle error
})
}
})
completionHandler()
}
}
I'm assuming this all a local notification.
AFAIK there is solution to your question!
When the notification arrives, you're either in foreground or background.
foreground: you get the userNotificationCenter(_:willPresent:withCompletionHandler:) callback but I don't think in that case you'll want to increase the badge right? Because the user has just seen it. Though I can imagine where you might need to do such. Suppose your app is like WhatsApp and the user has the app opened and is sending a message to his mother. Then a message from his father arrives. At this point he hasn't opened the messages between him and his father yet he sees the notification. In your willPresent you could query the getDeliveredNotifications and and adjust your badge count.
background: for iOS10+ version for local notifications you're out of luck! Because there is NO callback for you. The notification gets delivered to the OS and that's it! There use to be a named application:didReceiveLocalNotification:
but that's deprecated. For more on that see here
When user taps (foreground or backend) then you'll get the userNotificationCenter(_:didReceive:withCompletionHandler:)
but that has no use again because the user has already acknowledged receiving the notification and increasing the badge in this case doesn't make sense.
Long story short AFAIK there is nothing you can do for local notifications.
If it's a remote notification then in the application(_:didReceiveRemoteNotification:fetchCompletionHandler:) you can query the delivered notifications and increase the badge count...
EDIT:
Since the badgeCount is attached to the arriving notification, then if you can update its badgeCount prior to arrival then you're all good. e.g. at 12pm you can always query the list of pendingNotifications. It will give you all the notifications arriving after 12pm and update the badgeCount on them if necessary e.g. decrease their badgeCount if some delivered notifications are read. For a complete solution on this see see Carspen90's answer. The gist of his answer is
for any new notification you want to send:
get the pendingNotifications
filter notifications which their firingDate is sooner than the new to be sent notification and get its count
set the new notification's badge to app's badgeCount + filteredCount + 1
if any of the pending notifications have a firingDate greater than the new notification we just added then we will increase the badgeCount on the pending notification by 1.
obviously again whenever you interact with delivered notifications, then you have to get all pendingNotifications again and decrease their badgeCount by 1
CAVEAT:
You can't do such for notifications which their trigger is based on location because obviously they don't care about time.

Unable to update local scheduled notification content

In one of the WWDC sessions I got code snippet for updating existing notifications. I don't think it works. Trying to update notification content.
First I request pending notifications from UNUserNotificationCenter which always works. Then I am creating new request to update notification with existing unique identifier.
There's 1 new variable content: String.
// Got at least one pending notification.
let triggerCopy = request!.trigger as! UNTimeIntervalNotificationTrigger
let interval = triggerCopy.timeInterval
let newTrigger = UNTimeIntervalNotificationTrigger(timeInterval: interval, repeats: true)
// Update notificaion conent.
let notificationContent = UNMutableNotificationContent()
notificationContent.title = NSString.localizedUserNotificationString(forKey: "Existing Title", arguments: nil)
notificationContent.body = content
let updateRequest = UNNotificationRequest(identifier: request!.identifier, content: notificationContent, trigger: newTrigger)
UNUserNotificationCenter.current().add(updateRequest, withCompletionHandler: { (error) in
if error != nil {
print("🚫 Couldn't update notification \(error!.localizedDescription)")
}
})
I am unable to catch error. The problem is that notification content body doesn't change.
Update.
I also tried to change trigger with different repeat interval. It doesn't work, notification is repeated with the same original interval it was created with.
Update 2.
Read Chris' answer, trying to go with first option.
let center = UNUserNotificationCenter.current()
center.getPendingNotificationRequests(completionHandler: { (requests) in
for request in requests {
if request.identifier == notificationIdentifier {
// Got at least one pending notification,
// update its content.
let notificationContent = UNMutableNotificationContent()
notificationContent.title = NSString.localizedUserNotificationString(forKey: "new title", arguments: nil)
notificationContent.body = "new body"
request.content = notificationContent // ⛔️ request.content is read only.
}
}
})
As you can see I can't modify original request.
Update 3.
Had go with second "delete first" option. Noticed that calling removePendingNotificationRequests and schedule after, still gives me old notification version. I had to add 1 second delay between calling removePendingNotificationRequests and center.add(request).
Marked Chris' answer as accepted but feel free to share better option.
The problem is that you're not modifying the existing notification, and instead adding a new notification with a duplicate identifier.
Let's tackle the duplicate issue first, the reason this duplicate notification doesn't show up is because the identifier isn't unique. From the docs:
(if identifier is not unique, notifications are not delivered).
You have two options. You can 1) modify the existing Notification, or 2) remove it and add the new one.
For 1, you already have the request, instead of pulling the trigger and identifier out of it, just replace request.content with your updated notificationContent.
For 2, you would just need to add a line before your Add:
UNUserNotificationCenter.current().removePendingNotificationRequests(withIdentifiers: [request!.identifier])
After I've requested to Allow Notifications:
I trigger a notification right from my viewDidLoad but then also trigger another one with the same identifier. At the end the the updatedBody/updatedTitle show up.
import UIKit
import UserNotifications
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
let content = UNMutableNotificationContent()
content.title = "Scheduled Task"
content.body = "dumbBody"
content.badge = 1
content.sound = UNNotificationSound.default()
content.categoryIdentifier = "alertCategory"
UNUserNotificationCenter.current().delegate = self
//Setting time for notification trigger
let trigger = UNTimeIntervalNotificationTrigger.init(timeInterval: 3.0, repeats: false)
let request = UNNotificationRequest(identifier:"myIdentifier", content: content, trigger: trigger)
UNUserNotificationCenter.current().add(request, withCompletionHandler: {_ in print(" was registered")})
updateNotification()
}
My update function
func updateNotification(){
let center = UNUserNotificationCenter.current()
var request : UNNotificationRequest?
center.getPendingNotificationRequests{ notifications in
for notificationRequest in notifications{
if notificationRequest.identifier == "myIdentifier"{
request = notificationRequest
center.removeAllPendingNotificationRequests() // Removing this line or keeping it makes NO difference
}
}
let newTrigger = UNTimeIntervalNotificationTrigger.init(timeInterval: 5.0, repeats: false)
// Update notificaion conent.
let notificationContent = UNMutableNotificationContent()
notificationContent.title = "UpdatedTitle"
notificationContent.body = "updatedBody"
let updateRequest = UNNotificationRequest(identifier: request!.identifier, content: notificationContent, trigger: newTrigger)
UNUserNotificationCenter.current().add(updateRequest, withCompletionHandler: { (error) in
print("successfully updated")
if error != nil {
print("🚫 Couldn't update notification \(error!.localizedDescription)")
}
})
}
}
}
In the above snippet: Removing center.removeAllPendingNotificationRequests() would make no difference. Still I would receive the updatedNotification.
For handling incoming notifications
extension ViewController:UNUserNotificationCenterDelegate{
func userNotificationCenter(_ center: UNUserNotificationCenter, didReceive response: UNNotificationResponse, withCompletionHandler completionHandler: #escaping () -> Void) {
print("original identifier was : \(response.notification.request.identifier)")
print("original body was : \(response.notification.request.content.body)")
print("Tapped in notification")
switch response.actionIdentifier {
default:
print("some action was clicked")
}
}
func userNotificationCenter(_ center: UNUserNotificationCenter, willPresent notification: UNNotification, withCompletionHandler completionHandler: #escaping (UNNotificationPresentationOptions) -> Void) {
print("Notification being triggered")
completionHandler( [.alert,.sound,.badge])
}
}

Swift: how to schedule local notifications with dynamic content

I have a weather app for iOS, and I'd like to allow the user to receive a notification each morning at a time of their choosing which would fetch the weather forecast for the day and display a notification.
I'd like to avoid using push notifications, and I thought I might be able to use local notifications, except I can't see a way to fetch the content to be shown from a server. It looks like the content has to be set at the time of scheduling. Is that right?
That makes me think I might be able to register my application to use background execution to periodically fetch the weather and schedule a notification with the latest content, but this seems wasteful.
In short, I'd like to tell iOS to run a specific function at a specific time. Is there a good option for this that I'm missing? Are push notifications the only/best way to accomplish this sort of thing?
Push notification is best option for your if you want to display weather forecast .
More about this : https://stackoverflow.com/a/41901767/3901620
You can schedule a local notification for a specific time and when a user sees it and if he wants he can open your app by tapping on that notification. At that time, you will able to know that, a user has tapped on a notification and thus the app is open, you can make a network call to fetch the data and show it inside the application. This will not require any background calls therefor and only make a network call to an action by a user.
Another option: You can create a widget of your app (like Weather Widget). Whenever a user goes into widget area you will get a delegate call and make a network call to get the latest weather data. If a user wants more information on it, he can simply tap on it and your app will open. Then, everything will be in your hands.
Your option: You can always get dynamic content whenever the user opens your app for a particular date and set a notification for it. But this is not suggestible as the user may not get updated data.
Push Notification: This may not be required with your case, however, if you want to send the dynamic data over your server to your app. This is always the best option.
i have created a function. In which this will call your function at a specific time, when you want. Am creating a clock app so i need to trigger a local notification when ever user created the alarm. And in the notification Center Delegate method, you can handle your response and call the whatever method you want.
class LocalNotificationMethod : NSObject {
static let notificationInstance = LocalNotificationMethod()
let requestIdentifier = "SampleRequest" //identifier is to cancel the notification request
internal func scheduleLocalNotification(titleOfNotification:String, subtitleOfNotification:String, messageOfNotification:String, soundOfNotification:String, dateOfNotification:String) {
if #available(iOS 10.0, *) {
let formatter = DateFormatter()
formatter.dateFormat = "yyyy-MM-dd hh:mm a"
let date3 = formatter.date(from: dateOfNotification)
let content = UNMutableNotificationContent()
content.body = NSString.localizedUserNotificationString(forKey: titleOfNotification, arguments: nil)
content.sound = soundOfNotification.characters.count > 0 ? UNNotificationSound.init(named: soundOfNotification + ".mp3") : UNNotificationSound.default()
let trigger = UNCalendarNotificationTrigger.init(dateMatching: NSCalendar.current.dateComponents([.day, .month, .year, .hour, .minute], from: date3!), repeats: false)
let request = UNNotificationRequest(identifier:requestIdentifier, content: content, trigger: trigger)
UNUserNotificationCenter.current().add(request){(error) in
if (error != nil){
print(error?.localizedDescription)
} else {
print("Successfully Done")
}
}
} else {
// Fallback on earlier versions
}
}
}
And in AppDelegate Methods : - You can handle whenever user click on your notification or whenever your notification will be present.Is up to you what you want to done.
//MARK:- Notification Delegates
#available(iOS 10.0, *)
func userNotificationCenter(_ center: UNUserNotificationCenter, didReceive response: UNNotificationResponse, withCompletionHandler completionHandler: #escaping () -> Void) {
print("Tapped in notification")
}
//This is key callback to present notification while the app is in foreground
#available(iOS 10.0, *)
func userNotificationCenter(_ center: UNUserNotificationCenter, willPresent notification: UNNotification, withCompletionHandler completionHandler: #escaping (UNNotificationPresentationOptions) -> Void) {
print("Notification being triggered")
//You can either present alert ,sound or increase badge while the app is in foreground too with ios 10
//to distinguish between notifications
if notification.request.identifier == "SampleRequest" {
completionHandler( [.alert,.sound,.badge])
}
}

Resources