3

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)

}
Acode
  • 33
  • 6

1 Answers1

1

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.

PGDev
  • 23,751
  • 6
  • 34
  • 88
  • Just added my AppDelegate functions. Is it correct? – Acode Jun 17 '19 at 18:07
  • I meant that the AppDelegate functions I added to the Q were there before :). something else is probably not working well – Acode Jun 17 '19 at 18:14
  • Try the **Edit-1** and **Edit-2** that I've added the answer. – PGDev Jun 17 '19 at 18:16
  • When i tested it with the `let future = Date(timeIntervalSinceNow: 10)` it worked fine... but this is not my purpose of the code... the user is the one who's picking the date and the app suppose to send notification on that date... – Acode Jun 17 '19 at 18:17
  • I know this is not the purpose of your code. That's why I explicitly specified **just for debugging purpose**. Anyways, if it is working fine, then there must be an issue with the code where you're picking the date. Add that particular code to the question, – PGDev Jun 17 '19 at 18:20
  • that's what I thought too. can you explain what changes did you do in **Edit-2** and why? **thank u so much for trying to help me though** – Acode Jun 17 '19 at 18:22
  • It is almost the same. I just added it so we can find the actual issue. Now that we know it is not in the code present here, we need to look through the code that involved getting the `futureDate`. – PGDev Jun 17 '19 at 18:23
  • Do u want me to add the function where future is being used? – Acode Jun 17 '19 at 18:28
  • Added **Edit-3**. I think this will work for you perfectly. Try that. – PGDev Jun 17 '19 at 18:28
  • I just added `future` related code. how can I Apply your **Edit-3** code to this? – Acode Jun 17 '19 at 18:39
  • You don't need that much code to set `future` value. Simply create a `computed property` like I did and `return datePicker.date` from it. – PGDev Jun 18 '19 at 05:31
  • The thing is that my datePicker is nor set as an `@IBOutlet weak var datePicker: UIDatePicker!`. It is set as an `@IBAction func datePickerChanged(_ sender: UIDatePicker) { ` with a whole function in it... – Acode Jun 18 '19 at 10:46
  • `@IBAction` captures the action of any control. `@IBOutlet` is the property of the control that connects to your view. Create an `@IBoutlet` of your `datePicker`. – PGDev Jun 18 '19 at 11:19