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.
Oh, you want to run it. Let me give you the complete source code. Here
Thanks. But you sent only the .xcodeproj file, without any content file.
You have to link to a zip of the complete project folder.
Best practice for removePendingNotificationRequests with CoreData
 
 
Q