How to get a push notification at a set time? (Swift 3) - ios

I'm making an app that's supposed the user everyday at a set time about the news. It get's the text of the news through a function which calls it from an array. My question is: how do I get my app to call the function and then send me a push notification with the info text every day at, let's say, 4am?
Thanks to everyone for answering! Have a great day!

Here is some code I used before. Not a hundred-percent what you are looking for, but I hope useful for you.
You need to modify it to be sending daily
import UIKit
import UserNotifications
class ViewController: UIViewController, UNUserNotificationCenterDelegate {
var isGrantedNotificationAccess:Bool = false
#IBAction func send10SecNotification(_ sender: UIButton) {
if isGrantedNotificationAccess {
//add notification code here
//Set the content of the notification
let content = UNMutableNotificationContent()
content.title = "10 Second Notification Demo"
content.subtitle = "From MakeAppPie.com"
content.body = "Notification after 10 seconds - Your pizza is Ready!!"
//Set the trigger of the notification -- here a timer.
let trigger = UNTimeIntervalNotificationTrigger(
timeInterval: 10.0,
repeats: true)
//Set the request for the notification from the above
let request = UNNotificationRequest(
identifier: "10.second.message",
content: content,
trigger: trigger
)
//Add the notification to the currnet notification center
UNUserNotificationCenter.current().add(request, withCompletionHandler: nil)
}
}
override func viewDidLoad() {
super.viewDidLoad()
UNUserNotificationCenter.current().requestAuthorization(options: [.alert,.sound,.badge]) { (granted, error) in
self.isGrantedNotificationAccess = granted
}
}
}

Related

How do I schedule local notifications to fire once when the app opens without having repeats when the user reopens it? (Swift, XCode, IOS)

I'm new to programming in Swift and XCode and I'm having trouble debugging my code. The app is supposed to send notifications to the user once every day at the same time. I have the basics set up so far, but while testing, if I clear the app and reopen it, it schedules two notifications instead of just one. Is there some sort of magic trick I need to fix this?
Currently, the scheduleNotification function is being called under viewDidLoad so that it can schedule everything as soon as it boots up. I've tried calling the function under UNUserNotificationCenter.current().add(request). I figured it would make sense to call it under there because it only prompts the user once, so when the user grants notification permissions, it would activate the notifications immediately without risking repeats. But that didn't schedule any notifications at all.
I did some more research and maybe I'm programming the notifications in the wrong file? This is all in the View Controller.
I talked to my dad who's really into software and he suggested tracking the notifications with an ID and deleting them if I don't want them to appear. But that sounds very complicated and I'm not in the mood for another headache haha
Any ideas?
(Also, ignore some of the calls that have to do with the calculator. That's unrelated to my issue)
//
// ViewController.swift
//
import UIKit
class ViewController: UIViewController {
// Calculator Outlets
#IBOutlet weak var calculatorWorkings: UILabel!
#IBOutlet weak var calculatorResults: UILabel!
var workings:String = ""
override func viewDidLoad() {
super.viewDidLoad()
clearAll()
UNUserNotificationCenter.current().requestAuthorization(options: [.alert, .sound]) {_, _ in
DispatchQueue.main.asyncAfter(deadline: .now() + 1) {
print("Request authorized")
// Repeatedly call function so program schedules notifications in advance
for i in 1...10 {
self.scheduleNotification(day: i)
}
}
}
}
// Phrases
let phrasesArray = ["0", "1", "2", "3", "4", "5", "6", "7", "8"]
// Handle Notifications
func scheduleNotification(day: Int) {
var dateComponents = DateComponents()
dateComponents.hour = 21
dateComponents.minute = 09
dateComponents.day = day
let content = UNMutableNotificationContent()
// App name is not included in notification bar
content.title = "Jenna's Wisdom:"
content.body = phrasesArray[(Int.random(in: 0..<9))]
content.sound = .default
content.interruptionLevel = .timeSensitive
let trigger = UNCalendarNotificationTrigger(dateMatching: dateComponents, repeats: true)
// Request to send notifications
let request = UNNotificationRequest(identifier: UUID().uuidString, content: content, trigger: trigger)
UNUserNotificationCenter.current().add(request) { error in
if let error = error {
print(error)
} else {
print("Success")
}
}
}
}
Assuming ViewController is the first and rootViewcontroller you can just remove all local notifications before adding new ones.
UNUserNotificationCenter.current().removeAllPendingNotificationRequests()

UNCalendarNotificationTrigger not initiating

Okay - I am totally frustrated with this piece of code right now and ready to give up! Basically when simulating to either Simulator or actual device I get the requestAuthorisation to work no problem but the trigger does not initiate ever. I have followed several guys online and their code worked with ease! When I use a button to initiate a UNTimeIntervalNotificationTrigger it works but that is not what I want. Currently testing in iOS 14.3 as target for build. Rest of the App builds no problem. What am I doing wrong?! Cannot help but think that somewhere along the line of trying to get it to work I might have damaged something in info.plist or similar?! I have tested to repeat the trigger and not to repeat but neither works.
override func viewDidLoad() {
super.viewDidLoad()
//NOTIFICATIONS
// Step 1 - Ask the use for permission to notify
let randVerseCenter = UNUserNotificationCenter.current()
randVerseCenter.requestAuthorization(options: [.alert, .sound]){ (granted, error) in
if granted {
print("Yay - request authorisation worked!")
} else {
print ("D'oH - request Authorisation did not work!")
}
}
// Step 2 - Create the Notification Content
let randVerseContent = UNMutableNotificationContent()
randVerseContent.title = "Random Reference"
randVerseContent.body = "Random Verse"
randVerseContent.sound = UNNotificationSound.default
// Step 3 - Create the trigger for the notification by delay
let randVerseDate = Date().addingTimeInterval(30)
let randVerseDateComponents = Calendar.current.dateComponents([.year, .month, .day, .hour, .minute, .second], from: randVerseDate)
let randVerseTrigger = UNCalendarNotificationTrigger(dateMatching: randVerseDateComponents, repeats: true)
// Step 4 - Creating the request
let randVerseUUIDString = UUID().uuidString
let randVerseRequest = UNNotificationRequest(identifier: randVerseUUIDString, content: randVerseContent, trigger: randVerseTrigger)
// Step 5 - Register the request
randVerseCenter.add(randVerseRequest) { (error) in
if let error = error{
print (error.localizedDescription)
}
//Check the error parameter and handle any errors
}
}
After getting more details, I guess I know why you still don't see the notifications being delivered. I'm making it in another answer to not have it too long, but I'll keep my previous answer for reference.
Maybe you were waiting for the notification with the application in foreground? I'll refer to another part of the documentation:
Scheduling and Handling Local Notifications
On the section about Handling Notifications When Your App Is in the Foreground:
If a notification arrives while your app is in the foreground, you can
silence that notification or tell the system to continue to display
the notification interface. The system silences notifications for
foreground apps by default, delivering the notification’s data
directly to your app...
So, if that's the case, you must implement a delegate for UNUserNotificationCenter.
I suggest you something like this, where on AppDelegate you assign the delegate for UNUserNotificationCenter since documentation says it must be done before application finishes launching:
// AppDelegate.swift
#main
class AppDelegate: UIResponder, UIApplicationDelegate {
func application(_ application: UIApplication, willFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey : Any]? = nil) -> Bool {
UNUserNotificationCenter.current().delegate = self
return true
}
// Rest of your code on AppDelegate...
}
extension AppDelegate: UNUserNotificationCenterDelegate {
func userNotificationCenter(_ center: UNUserNotificationCenter, willPresent notification: UNNotification, withCompletionHandler completionHandler: #escaping (UNNotificationPresentationOptions) -> Void) {
// Here we actually handle the notification
print("Notification received with identifier \(notification.request.identifier)")
// So we call the completionHandler telling that the notification should display a banner and play the notification sound - this will happen while the app is in foreground
completionHandler([.banner, .sound])
}
}
On the view controller you have handling the notification authorization and request registration, you could do it like this:
class NotificationsViewController: UIViewController {
static let notificationAuthorizedNotification = NSNotification.Name(rawValue: "NotificationAuthorizedNotification")
let randVerseCenter = UNUserNotificationCenter.current()
override func viewDidLoad() {
super.viewDidLoad()
// We call this method when we know that the user granted permission, so we know we can then make notification requests
NotificationCenter.default.addObserver(self, selector: #selector(handleNotificationAuthorization), name: NotificationsViewController.notificationAuthorizedNotification, object: nil)
randVerseCenter.getNotificationSettings { [weak self] settings in
// We check current settings and asks for permission if not granted before
if settings.authorizationStatus == .notDetermined {
// Step 1 - Ask the use for permission to notify
self?.randVerseCenter.requestAuthorization(options: [.alert, .sound]){ (granted, error) in
if granted {
NotificationCenter.default.post(name: NotificationsViewController.notificationAuthorizedNotification, object: nil)
print("Yay - request authorisation worked!")
} else {
print ("D'oH - request Authorisation did not work!")
}
}
}
}
}
override func viewDidDisappear(_ animated: Bool) {
super.viewDidDisappear(animated)
// We stop listening to those notifications here
NotificationCenter.default.removeObserver(self)
}
#objc
func handleNotificationAuthorization() {
// Step 2 - Create the Notification Content
let randVerseContent = UNMutableNotificationContent()
randVerseContent.title = "Random Reference"
randVerseContent.body = "Random Verse"
randVerseContent.sound = UNNotificationSound.default
// Step 3 - Create the trigger for the notification by delay
let randVerseDate = Date().addingTimeInterval(30)
let randVerseDateComponents = Calendar.current.dateComponents([.second], from: randVerseDate)
let randVerseTrigger = UNCalendarNotificationTrigger(dateMatching: randVerseDateComponents, repeats: true)
// Step 4 - Creating the request
let randVerseUUIDString = UUID().uuidString
let randVerseRequest = UNNotificationRequest(identifier: randVerseUUIDString, content: randVerseContent, trigger: randVerseTrigger)
// Step 5 - Register the request
randVerseCenter.add(randVerseRequest) { (error) in
if let error = error{
print (error.localizedDescription)
} else {
print("Successfully registered notification with id \(randVerseUUIDString) at every second \(randVerseDateComponents.second!) of a minute")
}
}
}
}
You might still have older notifications scheduled since your code was requesting them at the viewDidLoad and maybe you didn't remove them or delete the app.
You can check the pending notifications using this on your viewDidLoad for example:
randVerseCenter.getPendingNotificationRequests() { requests in
for request in requests {
guard let trigger = request.trigger as? UNCalendarNotificationTrigger else { return }
print("Notification registered with id \(request.identifier) is schedulled for \(trigger.nextTriggerDate()?.description ?? "(not schedulled)")")
}
}
And use randVerseCenter to remove them by their identifiers or remove all of them.
The problem is how the trigger was created. We can look at the documentation for UNCalendarNotificationTrigger to get more understanding:
Create a UNCalendarNotificationTrigger object when you want to
schedule the delivery of a local notification at the specified date
and time. You specify the temporal information using an
NSDateComponents object, which lets you specify only the time values
that matter to you. The system uses the provided information to
determine the next date and time that matches the specified
information.
https://developer.apple.com/documentation/usernotifications/uncalendarnotificationtrigger
So, you use UNCalendarNotificationTrigger when you want to create a trigger to match the date components. The code below will create a trigger which will deliver a notification every day at 8:30 in the morning, because the .hour and the .minute components were specified:
var date = DateComponents()
date.hour = 8
date.minute = 30
// This trigger will match these two components - hour and minute
let trigger = UNCalendarNotificationTrigger(dateMatching: date, repeats: true)
In your case, you created a trigger using all of the components of a date (year, month, dat, hour, minute, second):
let randVerseDateComponents = Calendar.current.dateComponents([.year, .month, .day, .hour, .minute, .second], from: randVerseDate)
And that makes it an impossible condition to repeat the trigger - because there won't be another year 2021 - so it will not be triggered.
You need to think how you want this notification to be triggered. If your intention is to deliver a notification on the same second counting from a specific time, then you must use only the .second date component:
let randVerseDateComponents = Calendar.current.dateComponents([.second], from: randVerseDate)
Let's say randVerseDate is something like 2021-01-06-20:01:35, and we use the line of code above. Then this will trigger the notification every minute when the clock reaches 35 seconds: 20:02:35, then 20:03:35, then 20:04:35, and so on...

Calling removeAllPendingNotificationRequests() in applicationWillTerminate not working on iPad

I set up a dummy project to reproduce the issue I'm seeing. In my ContentView, I schedule some repeating notifications.
struct ContentView: View {
var body: some View {
VStack {
Button("Schedule notifications") {
let content = UNMutableNotificationContent()
content.title = "Title"
content.body = "body"
content.sound = UNNotificationSound.default
let trigger = UNTimeIntervalNotificationTrigger(timeInterval: 60, repeats: true)
let request = UNNotificationRequest(identifier: UUID().uuidString, content: content, trigger: trigger)
UNUserNotificationCenter.current().add(request, withCompletionHandler: nil)
}
Button("Request Permission") {
let current = UNUserNotificationCenter.current()
current.requestAuthorization(
options: [.sound, .alert],
completionHandler: { completed, wrappedError in
guard let error = wrappedError else {
return
}
})
}
}
}
}
Then in my AppDelegate, I attempt to cancel those repeating notifications before the app terminates.
func applicationWillTerminate(_ application: UIApplication) {
UNUserNotificationCenter.current().removeAllPendingNotificationRequests()
print("============== removing all notifications")
}
What I'm finding is that my scheduled notifications are still delivered, even though I can see my print statement in the Xcode console. But if I run the same test on an iPhone, my notification is not delivered, as expected.
Am I doing something wrong, or is this a bug? I'm using 13.4.1 on iPad, and 13.3.1 on iOS
I am also facing the same problem. Found out that applicationWillTerminate method has approximately five seconds to perform any tasks and return. And removeAllPendingNotificationRequests returns immediatly but removes scheduled notification asynchronously in secondary thread.
I think 5 seconds are more than necessary to clear notification.

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.

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.

Resources