0

I've been working on one of my project where I allow users to schedule multiple notifications at their desired time.

iOS 10 gave us the ability to use a DateComponents as the fire date for our notification but I'm kind of lost as to how I'm supposed to schedule and handle multiple notifications.

Each notification needs its own request which then in turn needs its own identifier else you cannot create multiple notifications.

I figured I had to work with the identifier to schedule and handle notifications, but now my code is such a mess that I've been seriously asking myself if there hasn't been an easier way of doing this.

According to my data model the notification unique identifier is composed of :

  1. The id of my model
  2. A number that increments each time a new notification is created
  3. The above is separated by an underscore

So for example if I had to schedule 10 notifications for the object with the id 3 it would look like this : 3_1, 3_2, 3_3...

Every time I receive a notification I loop trough the received notifications to update my UI. And when the user desires to delete the received notifications for a specific model, I loop through the received notification's unique identifiers by checking the identifiers that starts with the same ID.

I don't really see how I could manage to do it otherwise because according to the documentation, the only way of deleting a delivered notification is by using the identifier : UNUserNotificationCenter.current().removeDeliveredNotifications(withIdentifiers:)

The thing is, it's creating all sort of problems and while I could easily correct them it looks very hacky. I'm not really proud of what I've done and I am looking for more clever ways of getting around it. I intentionally did not post any code because that's not the problem. The approach is the real problem.

I'm asking here because the UserNotifications framework is pretty new and I haven't been able to find ressources taking about this subject.

Any idea ? Thanks in advance.

Edit : Here's some code.

@available(iOS 10.0, *)
    func checkDeliveredAndPendingNotifications(completionHandler: @escaping (_ identifierDictionary: Dictionary<String, Int>) -> ()) {

        var identifierDictionary:[String: Int] = [:]
        UNUserNotificationCenter.current().getDeliveredNotifications { (notifications) in

            for notification in notifications {
                let identifierArraySplit = notification.request.identifier.components(separatedBy: "_")
                if identifierDictionary[identifierArraySplit[0]] == nil || identifierDictionary[identifierArraySplit[0]]! < Int(identifierArraySplit[1])!  {
                    identifierDictionary[identifierArraySplit[0]] = Int(identifierArraySplit[1])
                }
            }

            UNUserNotificationCenter.current().getPendingNotificationRequests(completionHandler: { (requests) in
                for request in requests {
                    let identifierArraySplit = request.identifier.components(separatedBy: "_")
                    if identifierDictionary[identifierArraySplit[0]] == nil || Int(identifierArraySplit[1])! > identifierDictionary[identifierArraySplit[0]]!  {
                        identifierDictionary[identifierArraySplit[0]] = Int(identifierArraySplit[1])
                    }
                }
                completionHandler(identifierDictionary)
            })
        }
    }


@available(iOS 10.0, *) 
    func generateNotifications() {
        for medecine in medecines {
            self.checkDeliveredAndPendingNotifications(completionHandler: { (identifierDictionary) in
                DispatchQueue.main.async {
                    self.createNotification(medecineName: medecine.name, medecineId: medecine.id, identifierDictionary: identifierDictionary)
                    }                    
            })
        }
    }


@available(iOS 10.0, *)
    func createNotification(medecineName: String, medecineId: Int identifierDictionary: Dictionary<String, Int>) {

        let takeMedecineAction = UNNotificationAction(identifier: "TAKE", title: "Take your medecine", options: [.destructive])
        let category = UNNotificationCategory(identifier: "message", actions: [takeMedecineAction], intentIdentifiers:[], options: [])
        UNUserNotificationCenter.current().setNotificationCategories([category])

        let takeMedecineContent = UNMutableNotificationContent()
        takeMedecineContent.userInfo = ["id": medecineId]
        takeMedecineContent.categoryIdentifier = "message"
        takeMedecineContent.title = medecineName
        takeMedecineContent.body = "It's time for your medecine"
        takeMedecineContent.badge = 1
        takeMedecineContent.sound = UNNotificationSound.default()

        let trigger = UNTimeIntervalNotificationTrigger(timeInterval: 60, repeats: false)

        var takeMedecineIdentifier = ""
        for identifier in identifierDictionary {
            if Int(identifier.key) == medecineId {
                let nextIdentifierValue = identifier.value + 1
                takeMedecineIdentifier = String(medecineId) + "_" + String(nextIdentifierValue)
            }
        }
        let takeMedecineRequest = UNNotificationRequest(identifier: takeMedecineIdentifier, content: takeMedecineContent, trigger: trigger)

        UNUserNotificationCenter.current().add(takeMedecineRequest, withCompletionHandler: { (error) in
            if let _ = error {
                print("There was an error : \(error)")
            }
        })
    }

In the checkDeliveredAndPendingNotifications method, I loop through all the pending and delivered notifications, so later on I can create an identifier that does not exist already. I haven't found another way to generate unique identifiers for each notification.

As most of the work is done on another async queue, I've also created a completion handler when he has finished its job. I then call the createNotification method on the main thread (because I'm using Realm, and I'm obliged too do this) which should create a notification.

The problem here is the func add(UNNotificationRequest, withCompletionHandler: (Error?) -> Void)? = nil) method which is also doing work asynchronously. So when I go back to my loop in generateNotifications the checkDeliveredAndPendingNotifications return incorrect data. Well not incorrect it's just that the notification hasn't been created yet...

I'm a total noob with threading and I'm stuck with these kind of operations and I don't know where to go. I'm not sure I'm approaching the problem the right way.

Croisciento
  • 149
  • 1
  • 12
  • I don't really understand your problem, seems like identifier are required for local notifications, you should have post some code cause it's hard to see where it's getting messy – Damien Oct 11 '16 at 13:10
  • Please, explain what are you asking exactly. Where are you having trouble with? Have you seen examples such as this one? https://www.appcoda.com/ios10-user-notifications-guide/ – xpereta Oct 11 '16 at 13:10
  • If you want to get your hands on the new `UNUserNotificationCenter`, check this answer which I have posted. http://stackoverflow.com/questions/39713605/getting-local-notifications-to-show-while-app-is-in-foreground-swift-3/39715402#39715402 – Rajan Maheshwari Oct 11 '16 at 14:26
  • @damienBannerot Sorry for the late answer, I've been formatting my code to make my issues easier to understand. – Croisciento Oct 12 '16 at 14:07

1 Answers1

0

You can get all notification and set/Delete as you need. Have a look on this accepted answer for both Objc c and swift.

How to schedule a local notification in iOS 10 (objective-c)

Community
  • 1
  • 1
Jamshed Alam
  • 12,424
  • 5
  • 26
  • 49
  • I'm having specific issues with creating specific identifiers for each notification. I've updated my answer, sorry for being so vague. – Croisciento Oct 12 '16 at 14:09