Fire local notification when dead line is come - swift

In my to-do app, I'm trying to set local notification at the time when dead line date for task have been came, but i can't figure out whats wrong with calendar trigger, interval trigger working ok. In function body i put default Date()
func setupNotifications(id: String, contentTitle: String, contentBody: String, date: Date) {
center.getNotificationSettings { (settings) in
if (settings.authorizationStatus == .authorized) {
let content = UNMutableNotificationContent()
content.title = contentTitle
content.body = contentBody
content.sound = .default
let dateComponents = Calendar.current.dateComponents([.year, .month, .day, .hour], from: Date().addingTimeInterval(5))
let trigger = UNCalendarNotificationTrigger(dateMatching: dateComponents, repeats: false)
let trigger2 = UNTimeIntervalNotificationTrigger(timeInterval: 1, repeats: false)
let request = UNNotificationRequest(identifier: id, content: content, trigger: trigger)
let request2 = UNNotificationRequest(identifier: id, content: content, trigger: trigger2)
self.center.add(request)
}
}

You said:
let dateComponents = Calendar.current.dateComponents(
[.year, .month, .day, .hour], from: Date().addingTimeInterval(5)
)
A time interval is reckoned in seconds. But your dateComponents doesn't have any seconds; you've told it to consider only the year, the month, the day, and the hour. And that time is in the past.
You can see this simply by converting your date components back to a date:
let d = Date()
print(d) // 2022-12-01 14:24:02 +0000
let dateComponents = Calendar.current.dateComponents(
[.year, .month, .day, .hour],
from: d.addingTimeInterval(5)
)
let d2 = Calendar.current.date(from: dateComponents)
print(d2) // 2022-12-01 14:00:00 +0000
So at the very least you would need to include .seconds in your list of components. That, as you can see, changes everything:
import UIKit
let d = Date()
print(d) // 2022-12-01 14:25:22 +0000
let dateComponents = Calendar.current.dateComponents(
[.year, .month, .day, .hour, .second],
from: d.addingTimeInterval(5)
)
let d2 = Calendar.current.date(from: dateComponents)
print(d2) // 2022-12-01 14:00:27 +0000

found solution here
https://github.com/YoussifHany51/ToDoList/blob/main/ToDoList/Models/localNotification.swift
adopted to my project
func setupNotifications(id: String, deadline:Date?) {
if deadline != nil {
let calendar = Calendar.current
let hour = calendar.component(.hour, from: deadline ?? Date())
let minute = calendar.component(.minute, from: deadline ?? Date())
let second = calendar.component(.second, from: deadline ?? Date())
let day = calendar.component(.day, from: deadline ?? Date())
let month = calendar.component(.month, from: deadline ?? Date())
let content = UNMutableNotificationContent()
content.title = "To Do"
content.subtitle = "Today is deadline for: \(id)"
content.sound = .default
var operationDate = DateComponents()
operationDate.hour = hour
operationDate.minute = minute
operationDate.second = second
operationDate.month = month
operationDate.day = day
//Trigger
let trigger = UNCalendarNotificationTrigger(dateMatching: operationDate, repeats: false)
//Request
let requst = UNNotificationRequest(
identifier: id, content: content,trigger: trigger
)
UNUserNotificationCenter.current().add(requst)
}
}

Related

Start and End date in UNNotificationRequest

How to schedule a local notification request with the future start date(with Time) and end date(with Time). is it possible as I am unable to see any variable in the framework
let formatter = DateFormatter()
formatter.dateStyle = .long
formatter.dateFormat = "dd MMM yyyy HH:mm"
guard let startDate = formatter.date(from: "03 Sep 2021 09:40")
, let endDate = formatter.date(from: "03 Sep 2021 10:40") else { return }
let intervalBetweenNotifications: Double = 5 * 60 // 5 minutes
for item in stride(from: startDate.timeIntervalSinceNow, to: endDate.timeIntervalSinceNow, by: intervalBetweenNotifications) {
let objectId = UUID().uuidString
let content = UNMutableNotificationContent()
content.title = "Title of the notification"
content.sound = .default
content.threadIdentifier = objectId
let trigger = UNTimeIntervalNotificationTrigger(timeInterval: item, repeats: false)
let request = UNNotificationRequest(identifier: objectId, content: content, trigger: trigger)
UNUserNotificationCenter.current().add(request, withCompletionHandler: nil)
}
You can change the start, end date and the interval between consecutive notifications.

Swift: DatePicker-Timer?

Hey!
Here's my problem:
So I have a datepicker which is primarily used to send messages when the set time is reached. However, I would like to see the time run out (timer-like) until the time is reached. Best in the format (hh:mm:ss), because the datepicker is set in the timer format.
I am only a beginner in programming. So I don't know what to do :/
I would be very happy if someone could help me!
Many thanks in advance.
let date = NSDate()
let calendar = Calendar.current
let components = calendar.dateComponents([.hour, .minute, .month, .year, .day], from:
date as Date)
let currentDate = calendar.date(from: components)
let userCalendar = Calendar.current
let competitionDate = self.datepicker.isSelected
let competition = userCalendar.date(from: competitionDate as DateComponents)!
let CompetitionDifference = calendar.dateComponents([.hour, .minute, .second], from:
currentDate!, to: competition)
let hoursLeft = CompetitionDifference.hour
let minutesLeft = CompetitionDifference.minute
let secondsLeft = CompetitionDifference.second
print("hours:", hoursLeft ?? "N/A", "minutes:", minutesLeft ?? "N/A", "seconds:",
secondsLeft ?? "N/A")
countDownLabel.text = "\(daysLeft ?? 0) hours, \(hoursLeft ?? 0) minutes, \(minutesLeft
?? 0) seconds"
First, you just can't write :
let competition = userCalendar.date(from: competitionDate as DateComponents)!
it will just trigger an error if competitionDate is not a DateComponents but say, a Date!
Then, if you just want string representations of quantities of time DateComponentsFormatter is for you
try :
let date = Date()
let calendar = Calendar.current
let userCalendar = Calendar.current
let competitionDate = date + 4899
let comps = userCalendar.dateComponents([.year, .month, .day, .hour, .minute, .second], from: competitionDate)
let formatter = DateComponentsFormatter()
formatter.unitsStyle = .full
formatter.string(from: date, to: competitionDate) // "1 hour, 21 minutes, 39 seconds"
formatter.unitsStyle = .short
formatter.string(from: date, to: competitionDate) // "1 hr, 21 min, 39"secs"
formatter.unitsStyle = .brief
formatter.string(from: date, to: competitionDate) // "1hr 21min 39secs"
formatter.unitsStyle = .abbreviated
formatter.string(from: date, to: competitionDate) // "1h 21m 39s"
formatter.unitsStyle = .positional
formatter.string(from: date, to: competitionDate) // "1:21:39"
formatter.includesTimeRemainingPhrase = true
formatter.string(from: date, to: competitionDate) // "1:21:39 remaining"

How do you send a notification at a certain time that the user has entered in a text box in xcode 10?

I am trying to get a user to enter the time to take medicine or when they have an appointment in a text field and then send an alarm or notification at that time. I am a newbie so I don't know how to send notifications and such. I would need to do multiple of these depending on how many the user wants.
let content = UNMutableNotificationContent()
content.title = "Title"
content.body = "Body"
content.sound = UNNotificationSound.default()
let gregorian = Calendar(identifier: .gregorian)
let now = Date()
var components = gregorian.dateComponents([.year, .month, .day, .hour, .minute, .second], from: now)
// Change the time to 7:00:00 in your locale
components.hour = 7
components.minute = 0
components.second = 0
let date = gregorian.date(from: components)!
let triggerDaily = Calendar.current.dateComponents([.hour,.minute,.second,], from: date)
let trigger = UNCalendarNotificationTrigger(dateMatching: triggerDaily, repeats: true)
let request = UNNotificationRequest(identifier: CommonViewController.Identifier, content: content, trigger: trigger)
print("INSIDE NOTIFICATION")
UNUserNotificationCenter.current().add(request, withCompletionHandler: {(error) in
if let error = error {
print("SOMETHING WENT WRONG")
}
})

Date wont follow timezone

Hi I'm trying to implement date/time notification but the date is 2 hours off.
Code when selecting date
func SelectTime(sender: UIView){
formatter.timeZone = TimeZone.autoupdatingCurrent
let timePicker = ActionSheetDatePicker(title: "Time:", datePickerMode: UIDatePickerMode.time, selectedDate: userDate, doneBlock: {
picker, userDateWithTime, index in
self.formatter.timeZone = TimeZone.autoupdatingCurrent
self.formatter.dateFormat = "yyyy MMMM dd, HH:mm"
self.dateSelected.text = self.formatter.string(for: userDateWithTime)
//print("User date picked \(self.formatter.string(from: userDateWithTime as! Date))")
return
Code for creating the Notification
let uuid = UUID().uuidString
let notification = UNMutableNotificationContent()
notification.title = "Plus - Todo"
//notification.subtitle
notification.body = taskText.text!
let calendar = Calendar.current
let dateComponents = calendar.dateComponents(in: .current, from: userDateWithTime)
//let dateComponents = Calendar.current.dateComponents([.year, .month, .day, .hour, .minute], from: correctDate!)
let notificationTrigger = UNCalendarNotificationTrigger(dateMatching: dateComponents, repeats: false)
//let notificationTrigger = UNTimeIntervalNotificationTrigger(timeInterval: 5, repeats: false)
let request = UNNotificationRequest(identifier: uuid, content: notification, trigger: notificationTrigger)
UNUserNotificationCenter.current().add(request, withCompletionHandler: nil)
The problem was that userPickedDateWithTime had a type of Any and not Date. I solved this by creating a formattedDate with the type date and the set the formattedDate = userPickedDateWithTime as! Date and then used the formattedDate instead of userPickedDateWithTime when creating the notification
var formattedDate = Date()
func SelectTime(sender: UIView){
formatter.timeZone = TimeZone.autoupdatingCurrent
let timePicker = ActionSheetDatePicker(title: "Time:", datePickerMode: UIDatePickerMode.time, selectedDate: userDate, doneBlock: {
picker, userDateWithTime, index in
self.formatter.timeZone = TimeZone.autoupdatingCurrent
self.formatter.dateFormat = "yyyy MMMM dd, HH:mm"
self.dateSelected.text = self.formatter.string(for: userDateWithTime)
//print("User date picked \(self.formatter.string(from: userDateWithTime as! Date))")
formattedDate = userPickedDateWithtime as! Date // <-- This solved the problem
return
Notification code
let uuid = UUID().uuidString
let notification = UNMutableNotificationContent()
notification.title = "Plus - Todo"
//notification.subtitle
notification.body = taskText.text!
let calendar = Calendar.current
let dateComponents = calendar.dateComponents(in: .current, from: formattedDate)
//let dateComponents = Calendar.current.dateComponents([.year, .month, .day, .hour, .minute], from: correctDate!)
let notificationTrigger = UNCalendarNotificationTrigger(dateMatching: dateComponents, repeats: false)
//let notificationTrigger = UNTimeIntervalNotificationTrigger(timeInterval: 5, repeats: false)
let request = UNNotificationRequest(identifier: uuid, content: notification, trigger: notificationTrigger)
UNUserNotificationCenter.current().add(request, withCompletionHandler: nil)

How to make a countdown to date Swift

I was facing the struggle of making a timer app, so I thought that now that I solved it I could help others who face the problem. So basically this app counts down to a specific date from the current time. As stack overflow allows a Q and A format I hope that can help you. See the comments for explanations.
Cleaned up and updated with countdown computed on a timer and leading zero String format.
let futureDate: Date = {
var future = DateComponents(
year: 2020,
month: 1,
day: 1,
hour: 0,
minute: 0,
second: 0
)
return Calendar.current.date(from: future)!
}()
var countdown: DateComponents {
return Calendar.current.dateComponents([.day, .hour, .minute, .second], from: Date(), to: futureDate)
}
#objc func updateTime() {
let countdown = self.countdown //only compute once per call
let days = countdown.day!
let hours = countdown.hour!
let minutes = countdown.minute!
let seconds = countdown.second!
countdownLabel.text = String(format: "%02d:%02d:%02d:%02d", days, hours, minutes, seconds)
}
func runCountdown() {
Timer.scheduledTimer(timeInterval: 1, target: self, selector: #selector(updateTime), userInfo: nil, repeats: true)
}
Here is the solution of how I managed to create a countdown timer to a specific NSDate, for SO allows Q and A Style Answers.
// here we set the current date
let date = NSDate()
let calendar = NSCalendar.currentCalendar()
let components = calendar.components(.CalendarUnitHour | .CalendarUnitMinute | .CalendarUnitMonth | .CalendarUnitYear | .CalendarUnitDay, fromDate: date)
let hour = components.hour
let minutes = components.minute
let month = components.month
let year = components.year
let day = components.day
let currentDate = calendar.dateFromComponents(components)
// here we set the due date. When the timer is supposed to finish
let userCalendar = NSCalendar.currentCalendar()
let competitionDate = NSDateComponents()
competitionDate.year = 2015
competitionDate.month = 6
competitionDate.day = 21
competitionDate.hour = 08
competitionDate.minute = 00
let competitionDay = userCalendar.dateFromComponents(competitionDate)!
// Here we compare the two dates
competitionDay.timeIntervalSinceDate(currentDate!)
let dayCalendarUnit: NSCalendarUnit = (.CalendarUnitDay | .CalendarUnitHour | .CalendarUnitMinute)
//here we change the seconds to hours,minutes and days
let CompetitionDayDifference = userCalendar.components(
dayCalendarUnit, fromDate: currentDate!, toDate: competitionDay,
options: nil)
//finally, here we set the variable to our remaining time
var daysLeft = CompetitionDayDifference.day
var hoursLeft = CompetitionDayDifference.hour
var minutesLeft = CompetitionDayDifference.minute
Hope that helps you guys if you're facing the same struggle as I have
Cleaned up/updated for latest Swift version of the accepted answer.
// here we set the current date
let date = NSDate()
let calendar = Calendar.current
let components = calendar.dateComponents([.hour, .minute, .month, .year, .day], from: date as Date)
let currentDate = calendar.date(from: components)
let userCalendar = Calendar.current
// here we set the due date. When the timer is supposed to finish
let competitionDate = NSDateComponents()
competitionDate.year = 2017
competitionDate.month = 4
competitionDate.day = 16
competitionDate.hour = 00
competitionDate.minute = 00
let competitionDay = userCalendar.date(from: competitionDate as DateComponents)!
//here we change the seconds to hours,minutes and days
let CompetitionDayDifference = calendar.dateComponents([.day, .hour, .minute], from: currentDate!, to: competitionDay)
//finally, here we set the variable to our remaining time
let daysLeft = CompetitionDayDifference.day
let hoursLeft = CompetitionDayDifference.hour
let minutesLeft = CompetitionDayDifference.minute
print("day:", daysLeft ?? "N/A", "hour:", hoursLeft ?? "N/A", "minute:", minutesLeft ?? "N/A")
//Set countdown label text
countDownLabel.text = "\(daysLeft ?? 0) Days, \(hoursLeft ?? 0) Hours, \(minutesLeft ?? 0) Minutes"
This worked for me.
The only thing that troubles me is that it doesn't really countdown as the user has to refresh the page for it to recount. You can see it "counting" when the user is scrolling up and down cells on a UITableView as the cells do refresh the view.
Another thing is that I have on NSTimeZone of the currentDate "GMT+2:00" as it works for my time but only because I haven't figured out how to use the device NSTimeZone yet.
let releaseDate = "2015-05-02'T'22:00:00:000Z"
let futureDateFormatter = NSDateFormatter()
futureDateFormatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ss.SSSZ"
let date: NSDate = futureDateFormatter.dateFromString(releaseDate!)!
let currentDate = NSDate();
let currentFormatter = NSDateFormatter();
currentFormatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ss.SSSZ"
currentFormatter.timeZone = NSTimeZone(abbreviation: "GMT+2:00")
let diffDateComponents = NSCalendar.currentCalendar().components([NSCalendarUnit.Month, NSCalendarUnit.Day, NSCalendarUnit.Hour, NSCalendarUnit.Minute], fromDate: currentDate, toDate: date, options: NSCalendarOptions.init(rawValue: 0))
let countdown = "\(diffDateComponents.month) m: \(diffDateComponents.day) d: \(diffDateComponents.hour) h: \(diffDateComponents.minute) min"