Best practice for removePendingNotificationRequests with CoreData

Hello all,

I have a question regarding how to use the removePendingNotificationRequests when implemented in CoreData.

Currently, I have an app that lets users save meeting URLs. Each meeting the user creates is stored as a CoreData entity. They can set a time for these meetings, and the app will schedule a local notification for a few minutes before the meeting. The way I'm doing this is passing the meeting time the user selected into a sendNotification function and using that time as a trigger (UNCalendarNotificationTrigger).

Problem is -- once a user creates a meeting and the notification is set, the notification will happen even if the user deletes the meeting before the scheduled time. I want to use removePendingNotificationRequests to solve this problem. My question is: removePendingNotificationRequests accepts a string as the identifier for which notification request to remove. But, how do I pass this string to the request? And which string should I use? The meeting's name? I've been thinking about creating a new UUID attribute for my meeting entity so I'll always have a unique string, and passing that UUID to the onDelete function of the meetings.

Here's how I'm passing the meeting time to the notification function (this is inside a "Create" button in a AddNewMeeting view):

Code Block
self.notificationManager.sendNotification(title: "Upcoming Meeting", subtitle: nil, body: "\(meetingName) will start in 5 minutes. Tap here to get meeting information.", time: meetingTime, meetingName: "\(meetingName)")


This is my current code for the list of meetings:

Code Block
ForEach(items, id: \.self) { item in
       OneTimeRow(meeting: item)
}
.onDelete(perform: deleteItems)


And this is the delete function:

Code Block
    private func deleteItems(offsets: IndexSet) {
        withAnimation {
            offsets.map { items[$0] }.forEach(viewContext.delete)
            do {
                try viewContext.save()
            } catch {
                print("There was an error deleting items")
            }
        }
removePendingNotificationRequests(withIdentifiers identifiers: [String]) // <-- Is this how to put the function in?
    }

And this is the code for scheduling notifications:

Code Block
    func sendNotification(title: String, subtitle: String?, body: String, time: Date, meetingName: String) {
            let content = UNMutableNotificationContent()
            content.title = title
            if let subtitle = subtitle {
                content.subtitle = subtitle
            }
            content.body = body
            let triggerDate = time - 5 * 60
                let trigger = UNCalendarNotificationTrigger(
                            dateMatching: Calendar.current.dateComponents([.timeZone, .year, .month, .day, .hour, .minute], from: triggerDate),
                            repeats: true
                )
        let request = UNNotificationRequest(identifier: "\(meetingName)", content: content, trigger: trigger)
                UNUserNotificationCenter.current().add(request, withCompletionHandler: nil)
        }


Thank you!
Answered by Claude31 in 663611022
That is what you should use.

Of course, pass the actual Strings, with the exact meeting identifier
Code Block
removePendingNotificationRequests(withIdentifiers identifiers: [meetingID])


Note also :

This method executes asynchronously, removing the pending notification requests on a secondary thread.


Anyone have any ideas? Basically, I need a way to pass the meeting name of the meeting that is being deleted to the delete function.


Code Block
ForEach(items, id: \.self) { item in       
OneTimeRow(meeting: item)}
.onDelete(perform: deleteItems)


I'm going to include the following in the delete function:

Code Block
removePendingNotificationRequests(withIdentifiers identifiers: [String]



Accepted Answer
That is what you should use.

Of course, pass the actual Strings, with the exact meeting identifier
Code Block
removePendingNotificationRequests(withIdentifiers identifiers: [meetingID])


Note also :

This method executes asynchronously, removing the pending notification requests on a secondary thread.


Thank you, I'm now having trouble because I want to use the .onDelete(perform: ) to call two functions, the actual delete function then the delete notification function. I don't know how to call two functions, though:

Code Block
.onDelete(perform: deleteItems)


Here are my two functions:

Code Block
    private func deleteItems(offsets: IndexSet) {
        withAnimation {
            offsets.map { items[$0] }.forEach(viewContext.delete)
            do {
                try viewContext.save()
            } catch {
                print("There was an error deleting items")
            }
        }
    }
    private func removeSingleNotification(meetingID: String) {
        notificationManager.removePendingNotificationRequests(meetingID: meetingID)
    }


So I want to call both of these with the onDelete but I don't know how to include both functions because when I do, I get a compiler error that it can't type check in a reasonable amount of time. Thank you.
Did you try to include both functions inside deleteItems ?
Yes, I tried:

Code Block
.onDelete(perform: deleteItems, removeSingleNotification(meetingID: "\(item.meetingLink)"))


But it's giving me a "cannot type check in reasonable time" error.
Ok, actually, the error is that the 'items' is not in the scope of the function.

Code Block
ForEach(items, id: \.self) { item in  
OneTimeRow(meeting: item)
}
  .onDelete(perform: deleteItems, removeSingleNotification(meetingID: "\(item.meetingLink)"))

Any ideas about how to pass the specific item to the function? Thanks.
Where is items defined ?
Items is defined in a fetch request at the top of this view.

Code Block
    @FetchRequest(
        entity: Item.entity(),
        sortDescriptors: [NSSortDescriptor(keyPath: \Item.time, ascending: true)],
        animation: .default)
    private var items: FetchedResults<Item>


That would help to see the complete file, to understand why it is out of scope, and which function it is.
Code for content view:

Code Block
@Environment(\.managedObjectContext) private var viewContext
@FetchRequest(
        entity: Item.entity(),
        sortDescriptors: [NSSortDescriptor(keyPath: \Item.time, ascending: true)],
        animation: .default)
    private var items: FetchedResults<Item>
NavigationView {
                List {
                    Section(header:
                        Text("One-Time Meetings")
                            .textCase(nil)
                            .font(.headline)
                            .onAppear {
                                checkForUpdate()
                            }
                    ) {
                        ForEach(items, id: \.self) { item in
                            OneTimeRow(meeting: item)
                        }
                        .onDelete(perform: deleteItems, removeSingleNotification(meetingID: "\(item.meetingLink)"))
                    }
    private func deleteItems(offsets: IndexSet) {
        withAnimation {
            offsets.map { items[$0] }.forEach(viewContext.delete)
            do {
                try viewContext.save()
            } catch {
                print("There was an error deleting items")
            }
        }
    }
    private func removeSingleNotification(meetingID: String) {
        notificationManager.removePendingNotificationRequests(meetingID: meetingID)
        print("Deleted notification scheduled for meeting with identifier \(meetingID)")
    }

That's not the complete file.

I do not see the
Code Block
struct xxxx: View {


declaration for instance.

So please provide the complete file.
Is this your exact and complete project in the link ? Or a model from which you built your project ?
Yes. It's content view and the notifications file.
I downloaded and tried to run.

There is an error in Notifications:
Code Block
    var meetings: Item?

Item is not defined.
Best practice for removePendingNotificationRequests with CoreData
 
 
Q