How to fix repeated duplicate notifications problem in macOS app - swift

I have created a UNNotificationRequest with following code in a macOS app
let content = UNMutableNotificationContent()
content.title = "Welcome"
content.body = "body"
content.sound = UNNotificationSound.default
var dateComponent = DateComponents()
dateComponent.year = 2021
dateComponent.day = 24
dateComponent.hour = 12
dateComponent.minute = 27
let trigger = UNCalendarNotificationTrigger(dateMatching: dateComponent, repeats: true)
let request: UNNotificationRequest = UNNotificationRequest(identifier: UUID().uuidString, content: content, trigger: trigger)
UNUserNotificationCenter.current().add(request) { (error) in
}
I want the notification to trigger on 24th of every month in 2021.
When the notification is triggered, it generates repeated duplicate notifications every second.
If I make the year attribute in datecomponents to nil, then it does not generate repeated duplicate notifications.
But I need to set the year attribute according to my requirement. I tried the same code in a sample iOS app. It does not generate duplicate notifications. It generates only in macOS app.
What is the solution for not generating repeated duplicate notifications if I set the year attribute in datecomponents?

Related

Core Data and Notification Identifier SWIFTUI

I'm trying to create a notification using information from a user-created object in CoreData. However, I can't find a good property of the CoreData object to set as an identifier for the notification. I tried using the 'name' property of the object but there might be other objects with the same input so that is not a very safe approach.
How can I set a unique identifier for each notification that I can read from an object in CoreData?
My code is still too messy to post here but I can clear it up if you want.
Below is the latest approach I tried but this seems to be not working. It behaves as if ID is not a unique property.
let content = UNMutableNotificationContent()
content.title = "Reminder"
content.body = name
content.sound = UNNotificationSound.default
let identifier = "\(Reminder.ID.self)"
let calendar = Calendar.current
var reminderDate = DateComponents()
reminderDate = calendar.dateComponents([.year,.month,.day,.hour,.minute], from: remdate)
let trigger = UNCalendarNotificationTrigger(dateMatching: reminderDate, repeats: true)
let request = UNNotificationRequest(identifier: identifier, content: content, trigger: trigger)
UNUserNotificationCenter.current().add(request)

ios - waiting 24hrs to send a notification

Our application sends a notification every morning at 8am to greet the user and remind him to complete a task. Here is the script we are using:
let center = UNUserNotificationCenter.current()
center.removePendingNotificationRequests(withIdentifiers: ["dailyReminder"])
let content = UNMutableNotificationContent()
content.title = "Good morning"
content.body = notificationMessages[randomIndex]
content.sound = .default
var dateComponents = DateComponents()
dateComponents.hour = 8
let trigger = UNCalendarNotificationTrigger(dateMatching: dateComponents, repeats: true)
let request = UNNotificationRequest(identifier: "dailyReminder", content: content, trigger: trigger)
center.add(request)
This is called after the user finishes the day's task so that they are prompted the next day. However, some people finish the task before 8am. In those cases we'd like for the notification to be sent at 8am of the next day.
How could I calculate if the script runs before or after 8 and add 24 hrs accordingly?
Since this is run locally you could just save the last time the function was sent by holding it in a UserDefault. For example, add the following to the code to save when the user has last done a task:
UserDefaults.standard.set("/(youWillWriteAFunctionForDate)", forKey: "checkTaskOfDate")
Then you can simply check if it was sent today, and plan accordingly:
defaults.string(forKey: "checkTaskOfDate") ?? "Never Done"

How do you send notifications based on user input? SWIFT

How do you set the notification time to match the input of the user?
If a notification reminds the user of new content (i.e the word of the day), is that considered a local notification or a push notification?
The userSelectedTime variable is created when they press done, but I don't know how to pass it on to my sendNotification function which uses DateComponents() to set the time.
#objc func donePressed() {
// formatter
let formatter = DateFormatter()
formatter.dateStyle = .none
formatter.timeStyle = .short
var userSelectedTime = timeTextField.text
userSelectedTime = formatter.string(from: timePicker.date)
print(userSelectedTime)
self.view.endEditing(true)
}
func sendNotification() {
// Default time 10:30, but base it on userSelectedTime if not void
var dateComponents = DateComponents()
dateComponents.hour = 10
dateComponents.minute = 30
let trigger = UNCalendarNotificationTrigger(dateMatching: dateComponents, repeats: true)
let request = UNNotificationRequest(identifier: UUID().uuidString, content: createNotificationContent(for: myData), trigger: trigger)
center.add(request)
}
First I will answer your question 2. - This mostly depends on where that information is coming from. If the "word of the day" is something you have stored internally inside the app, you can schedule a local notification with that information. However, if you want to change the word of the day externally every day, you will have to create a push notification to achieve this. (At least the only way to make it work properly). Push notifications would require some sort of service to push those to the app so the easiest for you is to go with the local notifications.
To summarize question 2:
Local notification: Something that is created within the app.
Push notification: Information 'pushed' from an external source.
Question 1:
You can do this in a number of ways. What I would recommend from looking at what you already have is to create the date components with the selected time and pass those components to your 'sendNotification()' function.
As an example:
// Capture the time and turn them into components
func donePressed() {
let calendar = Calendar.current
let components = calendar.dateComponents([.hour, .minute, .month, .year, .day], from: timePicker.date)
sendNotification(components: components)
}
// Schedule the notificaiton based on components
func sendNotification(components: DateComponents) {
let trigger = UNCalendarNotificationTrigger(dateMatching: components, repeats: true)
let request = UNNotificationRequest(identifier: UUID().uuidString, content: createNotificationContent(for: myData), trigger: trigger)
center.add(request)
}
Now... I can't see all your code, so I assume that what else you got going is working properly. But I hope you get the concept. I would however recommend creating a separate class like "NotificationScheduler" to handle these things, that way you will be able to separate things a bit more.

Is it possible to mutate the content of a local notification in swift?

In the next version of my app, I want to integrate the possibility that the user can enable weekly reports like the screentime feature from apple.
I know how to setup Local Notifications (The code is already working for other types of notifications like reminders for events), but for this idea, I need the notification text to be updated shortly before firing because I don`t know the exact content of the notification when I add it to the NotificationCenter.
Is there a function which is called shortly before firing or how would I do this?
This is my working code for scheduling notifications:
fileprivate func generateNotification(title: String, body: String, triggerDate: DateComponents) -> String{
//Trigger
let trigger = UNCalendarNotificationTrigger(dateMatching: triggerDate, repeats: false)
//Content
let content = UNMutableNotificationContent()
content.title = title
content.body = body
content.sound = UNNotificationSound.default
//Request
let identifier = UUID().uuidString
let request = UNNotificationRequest(identifier: identifier, content: content, trigger: trigger)
let notificationCenter = UNUserNotificationCenter.current()
notificationCenter.add(request) { (error) in
if let error = error {
print("Error \(error.localizedDescription)")
}
}
return identifier
}

Change Repeating Reminder's Content Daily

In my app, I ask the user to set up a repeating reminder. In this reminder, I would like the body to say something different each day. For example, I have over 500 quotes in my Firebase db and I want my reminder to show a new quote each day. How can I programmatically change the body of the reminder each day without user interaction?
#IBAction func saveButtonPressed(_ sender: Any) {
let content = UNMutableNotificationContent()
let identifier = "myApp"
content.title = "myApp"
content.body = "I want to change this programatically each day"
let trigger = UNCalendarNotificationTrigger(dateMatching: Calendar.current.dateComponents([.year, .month, .day, .hour, .minute], from: reminderTime.date), repeats: true)
let request = UNNotificationRequest(identifier: identifier, content: content, trigger: trigger)
UNUserNotificationCenter.current().add(request){
(error) in
if error != nil
{
print("here error in setting up notification")
print(error!)
}
else
{
print("notification scheduled")
}
}
}
Unfortunately, you can not handle the LocalNotification showing moment. You just tell the system when to show your notification and the system show it without calling your application.
But you can add multiple notification requests at once (up to 64). For example, all notifications for the next month and update your notifications on app launch. Just make sure that your notification requests has different identifiers.
For example, you create 30 notifications and user receives them for 5 days. Then he opens your app and you add 5 notifications more.
If you'll need to remove some of the pending notifications, you can make it using the following method.
let center = UNUserNotificationCenter.current()
center.removePendingNotificationRequests(withIdentifiers: identifiers)