Search code examples
swiftnotificationsuilocalnotificationlocalnotification

Swift - Notifications on a certain date not shown


I'm making an app that users can create "memories" that have Title, Description, Date, and a Pic. After clicking 'save', I want the app to be able to notify the user on the date he picked that his event starts. I tried this code but it's not working. il'l be glad if you could fix my code or help me find the problem :)

future = sender.date (sender inside a UIDatePicker)

(and of course I wrote import UserNotifications )

@IBAction func saveMemorey(_ sender: UIButton) {        

    // User Notification code
    let center = UNUserNotificationCenter.current()
    let content = UNMutableNotificationContent()

    content.title = "New MEmorey!"
    content.subtitle = "A New Event Starts Today:"
    content.body = txtTitle.text!

    content.sound = UNNotificationSound.default
    content.threadIdentifier = "local-notifications temp"

        let dateComponents = Calendar.current.dateComponents([.year, .month, .day, .hour, .minute, .second], from: future)

        let trigger = UNCalendarNotificationTrigger(dateMatching: dateComponents, repeats: false)

        let request = UNNotificationRequest(identifier: "content", content: content, trigger: trigger)

        center.add(request) { (error) in
            if error != nil {
                print (error)
        }
    }

    self.navigationController?.popViewController(animated: true) // Returns to the memories page after clicking 'save'
}

AppDeligate:

class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterDelegate {

var window: UIWindow?

func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
    // Override point for customization after application launch.
    FirebaseApp.configure()

    let center = UNUserNotificationCenter.current()
    let options : UNAuthorizationOptions = [.sound, .alert]

    center.requestAuthorization(options: options) { (granted, error) in
        if error != nil {
            print (error)
        }
    }

    center.delegate = self
    return true
}

func userNotificationCenter(_ center: UNUserNotificationCenter, willPresent notification: UNNotification, withCompletionHandler completionHandler: @escaping (UNNotificationPresentationOptions) -> Void) {
    completionHandler([.alert, .badge, .sound])
}

future related:

 class AddMemoryViewController: UIViewController,UIImagePickerControllerDelegate,UINavigationControllerDelegate {

var future = Date()
var dateToSet : Double = 0.0

// connections from storyboard to the code

@IBOutlet weak var countLabel: UILabel!

@IBAction func datePickerChanged(_ sender: UIDatePicker) {
     future = sender.date

    //Use midnight today as the starting date
    guard let today = Calendar.current.date(bySettingHour: 0, minute: 0, second: 0, of: Date()) else { return }

    //Calculate the number of days between today and the =user's chosen day.
    let difference = Calendar.current.dateComponents([.day], from: today, to: future)
    guard let days = difference.day else { return }
    let ess = days > 1 ? "s" : ""
    if (days > 0)
    {
        countLabel.text = "That date is \(days) day\(ess) away."
    }
    if (days < 0)
    {
        countLabel.text = " \(abs(days)) day\(ess) since the event."
    }
    if (days == 0)
    {
        countLabel.text = " The event is today!"
    }
    dateToSet = Double(self.future.millisecondsSince1970)

}

Solution

  • In AppDelegate, you need to first request authorization from the user to send notifications to the device in application(_:didFinishLaunchingWithOptions:) method, i.e.

    func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
        UNUserNotificationCenter.current().requestAuthorization(options: [.alert,.sound]) { (allowed, error) in
            if allowed {
                print("User has allowed notifications")
            } else {
                print("User has declined notifications")
            }
        }
        return true
    }
    

    In the above code, you can provide the options as per your requirement. Refer this link to know about more possible options.

    Then, once the user authorization is successfully, you can schedule the notifications using your code.

    Edit-1:

    Just for debugging, execure the code by setting future value as:

    let future = Date(timeIntervalSinceNow: 10)
    

    This with fire the notification after 10 seconds from the current Date().

    Edit-2:

    The saveMemory action goes like,

    @IBAction func saveMemorey(_ sender: UIButton) {
        let content = UNMutableNotificationContent()
        content.title = "New Memory!"
        content.subtitle = "A New Event Starts Today:"
        content.body = ""
        content.sound = .default
    
        let dateComponents = Calendar.current.dateComponents([.year, .month, .day, .hour, .minute, .second], from: future)
        let trigger = UNCalendarNotificationTrigger(dateMatching: dateComponents, repeats: false)
    
        let request = UNNotificationRequest(identifier: "content", content: content, trigger: trigger)
        UNUserNotificationCenter.current().add(request) { (error) in
            if error != nil {
                print (error)
            }
        }
    }
    

    Edit-3:

    Here is how I'm getting the future date using UIDatePicker

    class VC: UIViewController {
        @IBOutlet weak var datePicker: UIDatePicker!
    
        var future: Date {
            return self.datePicker.date
        }
    
        @IBAction func saveMemorey(_ sender: UIButton) {
            //your code here....
        }
    
        //rest of the code...
    }
    

    In the above code, future is a computed property that returns whatever date is set in the datePicker at that point of time.