Multiple local notifications

Hello chris

I watched the tutorial for Local Notifications. My question is how can i add multiple daily notifications at different times with different content . Also what do you recommend if the app is based on reminders notifications for i.e should i focus on badge , design or can i add image etc…

Welcome @hussain64

Yes, I just finished an app that allows users to set the time of 1-4 notifications. Took an entire page of code a several methods to do what seemed like a simple task! Lol I would be happy to share my solution if you like.

Blessings,
—Mark

Great to hear that ! :+1:t2:Yes please that would be a much help

Here you go: you need to sort out how I implement all this. I have a switch that allows the user to turn off/on notifications. What surprised me about this is all the extra code it would take to handle the various times users would set or unset notifications.

import UIKit
import RealmSwift
import UserNotifications

class NotificationVC: UIViewController, UIPickerViewDelegate, UIPickerViewDataSource {
    
    
    @IBOutlet weak var morning: UITextField!
    @IBOutlet weak var noon: UITextField!
    @IBOutlet weak var evening: UITextField!
    @IBOutlet weak var night: UITextField!
    @IBOutlet weak var notificationSwitch: UISwitch!
    
    @IBOutlet weak var morningDelOutlet: UIButton!
    @IBOutlet weak var noonDelOutlet: UIButton!
    @IBOutlet weak var eveDelOutlet: UIButton!
    @IBOutlet weak var nightDelOutlet: UIButton!
    
    @IBOutlet weak var setNotificationBtn: UIButton!
    @IBOutlet weak var viewNotityOutlet: UIButton!
    
    @IBOutlet weak var showNotifyOutlet: UIButton!
    
    var prayers:Results<Prayers>?
    var timePicker = UIDatePicker()
    var timeFormatter = DateFormatter()
    var toolBar = UIToolbar()
    
    override func viewDidLoad() {
        super.viewDidLoad()

        //round button corners
        setNotificationBtn.layer.cornerRadius = 7
        setNotificationBtn.clipsToBounds = true
        showNotifyOutlet.layer.cornerRadius = 7
        showNotifyOutlet.clipsToBounds = true
        viewNotityOutlet.layer.cornerRadius = 7
        viewNotityOutlet.clipsToBounds = true
        
        
        //set up times for notifications
        morning.inputView = timePicker
        noon.inputView = timePicker
        evening.inputView = timePicker
        night.inputView = timePicker
        
        toolBar.sizeToFit()
        let doneButton = UIBarButtonItem(barButtonSystemItem: .save, target: self, action: #selector(saveTapped))
        toolBar.setItems([doneButton], animated: true)
        morning.inputAccessoryView = toolBar
        noon.inputAccessoryView = toolBar
        evening.inputAccessoryView = toolBar
        night.inputAccessoryView = toolBar
        
        timePicker.datePickerMode = .time
        
    } //end viewDidLoad
    
    override func viewWillAppear(_ animated: Bool) {
        
        let realm = try! Realm()
        let e = realm.object(ofType: Time.self, forPrimaryKey: "Primary")
        
        if e != nil {
            
            notificationSwitch.isOn = e!.reminder
            morning.text = e?.time1
            noon.text = e?.time2
            evening.text = e?.time3
            night.text = e?.time4
        }
    }
     
    
    func textFieldShouldBeginEditing(_ textField: UITextField) {
 setNotificationBtn.isEnabled = false
 setNotificationBtn.alpha = 0.3

    }
    
    @objc func saveTapped() {
        
        if notificationSwitch.isOn {
             TimeService.setReminder(reminder: true)
        } else {
             TimeService.setReminder(reminder: false)
        }
        
        if morning.isFirstResponder {
            if morning != nil {
                timeFormatter.timeStyle = .short
                morning.text = timeFormatter.string(from: timePicker.date)
                morning.resignFirstResponder()
                if morning != nil {
                    TimeService.addTime(new: morning.text!, prayerTime: "time1")
                }
                
            }
        }
        if noon.isFirstResponder {
            if noon != nil {
                timeFormatter.timeStyle = .short
                noon.text = timeFormatter.string(from: timePicker.date)
                noon.resignFirstResponder()
                if noon != nil {
                    TimeService.addTime(new: noon.text!, prayerTime: "time2")
                }
            }
        }
        if evening.isFirstResponder {
            if evening != nil {
                timeFormatter.timeStyle = .short
                evening.text = timeFormatter.string(from: timePicker.date)
                evening.resignFirstResponder()
                if evening != nil {
                    TimeService.addTime(new: evening.text!, prayerTime: "time3")
                }
            }
        }
        if night.isFirstResponder {
            if night != nil {
                timeFormatter.timeStyle = .short
                night.text = timeFormatter.string(from: timePicker.date)
                night.resignFirstResponder()
                if night != nil {
                   TimeService.addTime(new: night.text!, prayerTime: "time4")
                }
            }
        }
        
        setNotificationBtn.isEnabled = true
        setNotificationBtn.alpha = 1
    }
    

    
    
    @IBAction func notificationSwitchTapped(_ sender: Any) {
        
        if notificationSwitch.isOn == true {
            
            //set Realm object reminder to true
            //enable and brighten all user input items
            TimeService.setReminder(reminder: true)
            
            //enable and brighten all user input items
            setNotificationBtn.isEnabled = true
            setNotificationBtn.alpha = 1
            morning.isEnabled = true
            morning.alpha = 1
            morningDelOutlet.isEnabled = true
            morningDelOutlet.alpha = 1
            noon.isEnabled = true
            noon.alpha = 1
            noonDelOutlet.isEnabled = true
            noonDelOutlet.alpha = 1
            evening.isEnabled = true
            evening.alpha = 1
            eveDelOutlet.isEnabled = true
            eveDelOutlet.alpha = 1
            night.isEnabled = true
            night.alpha = 1
            nightDelOutlet.isEnabled = true
            nightDelOutlet.alpha = 1
            viewNotityOutlet.isEnabled = true
            viewNotityOutlet.alpha = 1
            showNotifyOutlet.isEnabled = true
            showNotifyOutlet.alpha = 1
            
            
        } else {
            
            //disable and fade all user input items
            setNotificationBtn.isEnabled = false
            setNotificationBtn.alpha = 0.3
            morning.isEnabled = false
            morning.alpha = 0.3
            morningDelOutlet.isEnabled = false
            morningDelOutlet.alpha = 0.3
            noon.isEnabled = false
            noon.alpha = 0.3
            noonDelOutlet.isEnabled = false
            noonDelOutlet.alpha = 0.3
            evening.isEnabled = false
            evening.alpha = 0.3
            eveDelOutlet.isEnabled = false
            eveDelOutlet.alpha = 0.3
            night.isEnabled = false
            night.alpha = 0.3
            nightDelOutlet.isEnabled = false
            nightDelOutlet.alpha = 0.3
            viewNotityOutlet.isEnabled = false
            viewNotityOutlet.alpha = 0.3
            showNotifyOutlet.isEnabled = false
            showNotifyOutlet.alpha = 0.3
            
            // set Realm object reminder to false
            TimeService.setReminder(reminder: false)
            
            //delete all current notifications
            let center = UNUserNotificationCenter.current()
            center.removeAllDeliveredNotifications()
            // To remove all delivered notifications
            center.removeAllPendingNotificationRequests()
            // To remove all pending notifications which are not delivered yet but scheduled.
        }
    }
    
    
    
    @IBAction func SetNotificationTapped(_ sender: Any) {
        
        UNUserNotificationCenter.current().getNotificationSettings { (settings) in
            
            switch settings.authorizationStatus {
            case .notDetermined:
                
                let center = UNUserNotificationCenter.current()
                
                center.requestAuthorization(options: [.alert, .sound, .badge]) { (granted, error) in
                    if granted {
                        
                        //this is where we set the notifications, first check if there are times set
                        
                        let realm = try! Realm()
                        let e = realm.object(ofType: Time.self, forPrimaryKey: "Primary")
                        
                        if e ==  nil {
                            DispatchQueue.main.async {
                                self.errorCatch(tile: "Attention!", message: "You must first set at least one prayer time before the app can remind you. ")
                            }
                            
                        } else {
                            //the FIRST time through, user has allowed notifications and has set at least one time.
                            if e?.time1 != "" {
                                self.setReminderTime(s: e!.time1, id: "Morning Reminder", title: "Morning Prayers")
                            }
                            
                            if e?.time2 != "" {
                                self.setReminderTime(s: e!.time2, id: "Noon Reminder", title: "Noon Day Prayers")
                            }
                            
                            if e?.time3 != "" {
                                self.setReminderTime(s: e!.time3, id: "Evening Reminder", title: "Evening Prayers")
                            }
                            if e?.time4 != "" {
                                self.setReminderTime(s: e!.time4, id: "Night Reminder", title: "Night Prayers (Compline)")
                            }
                        }
                    }
                }
            case .authorized, .provisional:
                
                //Warn user that at least one time must be set
                let realm = try! Realm()
                let e = realm.object(ofType: Time.self, forPrimaryKey: "Primary")
                
                if e == nil {
                    DispatchQueue.main.async {
                        self.errorCatch(tile: "Attention!", message: "You must first set at least one prayer time before the app can remind you. ")
                    }
                } else {
                    
                    //Uer allows notifications and has set at least one time
                    
                    if e?.time1 != "" {
                        self.setReminderTime(s: e!.time1, id: "Morning Reminder", title: "Morning Prayers")
                    }
                    
                    if e?.time2 != "" {
                        self.setReminderTime(s: e!.time2, id: "Noon Reminder", title: "Noon Day Prayers")
                    }
                    
                    if e?.time3 != "" {
                        self.setReminderTime(s: e!.time3, id: "Evening Reminder", title: "Evening Prayers")
                    }
                    if e?.time4 != "" {
                        self.setReminderTime(s: e!.time4, id: "Night Reminder", title: "Night Prayers (Compline)")
                    }
                }
            case .denied:
                DispatchQueue.main.async {
                    self.errorCatch(tile: "Attention!", message: "If you want to receive prayer reminders, you must grant access to notifications. To do this at anytime, go to: Settings > Notifications and scroll down to 'My Prayers' and tap on it to grant access in the future.")
                }
            default:
                print("hit break")
                break
            }
        }
    } //end button
    
    
    
    func setReminderTime(s:String, id:String, title:String) {
        
        let hour1 = self.getHour(h: s)
        let minute1 = self.getMinute(m: s)
        let h = hour1.toInt()
        let m = minute1.toInt()
        
        self.createReminder(i: id, t: title, h: h!, m: m!)
    }
    
    
    
    @IBAction func scheduleOfNotifications(_ sender: Any) {
        
        var data = ""
        var data2 = [String]()
        UNUserNotificationCenter.current().getPendingNotificationRequests(completionHandler: {requests -> () in
            data = ("\n\(requests.count) Notifications")
            for request in requests{
                data2.append((request.identifier))
            }
            
            let newData = data2.toPrint
            
            DispatchQueue.main.async {
                self.errorCatch(tile: "Prayer Reminders", message: "\(data) \n\(newData)")
            }
        })
    }
    
    
    
    func createReminder(i:String, t:String, h:Int, m:Int) {
        
        let manager = LocalNotificationsManager()
        manager.notifications = [
            Notification(id: i, title: t, datetime: DateComponents(calendar: Calendar.current, hour: h, minute: m))
        ]
        manager.schedule()
        
        DispatchQueue.main.async {
            self.errorCatch(tile: "Confirmed!", message: "Your daily reminder(s) have been schedule. To delete the reminders, tap the 'Delete Reminders' button below.")
        }
    }
  
    
    
    @IBAction func morningDelete(_ sender: Any) {
        TimeService.realmDelete(code: "time1")
        morning.text = ""
    }
    
    @IBAction func noonDelete(_ sender: Any) {
        TimeService.realmDelete(code: "time2")
        noon.text = ""
    }
    
    @IBAction func eveningDelete(_ sender: Any) {
        TimeService.realmDelete(code: "time3")
        evening.text = ""
    }
    
    @IBAction func nightDelete(_ sender: Any) {
        TimeService.realmDelete(code: "time4")
        night.text = ""
    }
    
    
    
    @IBAction func privacyButtonTapped(_ sender: Any) {
        let url = URL (string: "https://foundationsoftware.org/privacy_prayers.pdf")!
        UIApplication.shared.open(url)
    }
    
    //Reminders delte button
    
    @IBAction func viewNotificationTapped(_ sender: Any) {
        
       //delete all current notifications
       let center = UNUserNotificationCenter.current()
       center.removeAllDeliveredNotifications() // To remove all delivered notifications
       center.removeAllPendingNotificationRequests() // To remove all pending notifications which are not delivered yet but scheduled.
    
        DispatchQueue.main.async {
            self.errorCatch(tile: "Confirmed!", message: "All of your daily reminders have been removed.")
        }
    }
 
    
    // Mark: picker Methods
    
    func numberOfComponents(in pickerView: UIPickerView) -> Int {
        return 1
    }
    
    func pickerView(_ pickerView: UIPickerView, numberOfRowsInComponent component: Int) -> Int {
        return 1
    }
    
    
    
    //Mark: Utillity Methods
    
    func errorCatch(tile:String, message:String){
        
        let myAlert:UIAlertController = UIAlertController(title: tile, message: message, preferredStyle: UIAlertController.Style.alert)
        
        UILabel.appearance(whenContainedInInstancesOf: [UIAlertController.self]).numberOfLines = 0
        
        //create an action alert
        let secondAlertAction:UIAlertAction = UIAlertAction(title: "OK", style: UIAlertAction.Style.default, handler: nil)
        
        
        //add the alert action
        myAlert.addAction(secondAlertAction)
        
        //new for iPad alerts
        if let popoverController = myAlert.popoverPresentationController {
            popoverController.sourceView = self.view
            popoverController.sourceRect = CGRect(x: self.view.bounds.midX, y: self.view.bounds.midY, width: 0, height: 0)
            popoverController.permittedArrowDirections = []
        }
        //Present the alert
        self.present(myAlert, animated: true, completion: nil)
    } //end alertMessage
    
    
    
    //MARK: methods to covert strings and time
    
    func getHour(h:String) -> String {
        
        let dateAsString = h
        let dateFormatter = DateFormatter()
        dateFormatter.dateFormat = "h:mm a"
        let date = dateFormatter.date(from: dateAsString)

        dateFormatter.dateFormat = "HH"
        let hour = dateFormatter.string(from: date!)
        
        return hour
    }

    func getMinute(m:String) -> String {
        
        let dateAsString = m
        let dateFormatter = DateFormatter()
        dateFormatter.dateFormat = "h:mm a"
        let date = dateFormatter.date(from: dateAsString)

        dateFormatter.dateFormat = "mm"
        let minute = dateFormatter.string(from: date!)
        
        return minute
    }
    
} //end class

extension String {
    //Converts String to Int
    public func toInt() -> Int? {
        if let num = NumberFormatter().number(from: self) {
            return num.intValue
        } else {
            return nil
        }
    }
}

extension Array {
    var toPrint: String  {
        var str = ""
        for element in self {
            str += "\n\(element) "
        }
        return str
    }
}

This is a separate file:

import Foundation
import UserNotifications

class LocalNotificationsManager {
    
    var notifications = [Notification]()
      
    func schedule() {
        UNUserNotificationCenter.current().getNotificationSettings { (settings) in
            switch settings.authorizationStatus {
            case .notDetermined:
                break
            case .authorized, .provisional:
                self.scheduleNotifications()
            default:
                break
            }
        }
    }
    
    
    func scheduleNotifications() {
        
        for notification in notifications {
            
            let content = UNMutableNotificationContent()
            content.title = notification.title
            content.sound = .default
            
            
            let trigger = UNCalendarNotificationTrigger(dateMatching: notification.datetime, repeats: true)
            
            let request = UNNotificationRequest(identifier: notification.id, content: content, trigger: trigger)
            
            UNUserNotificationCenter.current().add(request) { (error) in
                guard error == nil else { return }
  
            }
        }
    }
} //end class

struct Notification {
       var id: String
       var title:String
       var datetime:DateComponents
   }

Let me know what is confusing as I did not really comment much of this as I have a separate system I use for commenting on code.
Blessings,
—Mark

1 Like

I need to say, I would have NEVER been able to take on this kind of app if it were not for Chris’ extremely fine teaching method. I am eternally grateful for this fantastic resource!

Blessings,
—Mark

1 Like