Date wont follow timezone - swift

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)

Related

Fire local notification when dead line is come

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)
}
}

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.

How to schedule local notification from iOS14 widget?

I would like to schedule a local notification from iOS14 widget.
How can I do it?
I tried implementing the following:
let center = UNUserNotificationCenter.current()
center.delegate = self
let content = UNMutableNotificationContent()
content.title = "Some Title"
content.body = "Some Body"
let trigger = UNTimeIntervalNotificationTrigger(timeInterval: 5, repeats: true)
let request = UNNotificationRequest(identifier: UUID().uuidString,
content: content, trigger: trigger)
center.add(request) { (error) in
if error != nil {
// Handle any errors.
}
I did with this.
func createDate(day: Int, month : Int, hour: Int, minute: Int, year: Int)->Date{
var components = DateComponents()
components.hour = hour
components.minute = minute
components.year = year
components.day = day
components.month = month
components.timeZone = .current
let calendar = Calendar(identifier: .gregorian)
return calendar.date(from: components)!
}
// 📢///CreateNitification
func scheduleNotification(at date: Date, identifierUnic : String, body: String, titles:String) {
let triggerWeekly = Calendar.current.dateComponents([.day, .month, .hour,.minute, .year], from: date)
let trigger = UNCalendarNotificationTrigger(dateMatching: triggerWeekly, repeats: true)
let content = UNMutableNotificationContent()
content.title = titles
content.body = body
content.sound = UNNotificationSound.default
content.categoryIdentifier = "todoList2"
let request = UNNotificationRequest(identifier: identifierUnic, content: content, trigger: trigger)
// UNUserNotificationCenter.current().delegate = self
/// UNUserNotificationCenter.current().removeDeliveredNotifications(withIdentifiers: ["textNotification2"])
/// UNUserNotificationCenter.current().removeAllPendingNotificationRequests()
UNUserNotificationCenter.current().add(request) {(error) in
if let error = error {
print(" We had an error: \(error)")
}}
}
// Call
func callNotifications(){
UNUserNotificationCenter.current().removeAllPendingNotificationRequests()
let adte = createDate(day: 3, month: 6, hour: 16, minute: 51, year: 2021)
scheduleNotification(at: adte, identifierUnic: UUID().uuidString, body: "Widgets on Work", titles: "check 123")
}
func getTimeline(in context: Context, completion: #escaping (Timeline<Entry>) -> ()) {
callNotifications()
var entries: [SimpleEntry] = []
// Generate a timeline consisting of five entries an hour apart, starting from the current date.
let currentDate = Date()
for hourOffset in 0 ..< 5 {
let entryDate = Calendar.current.date(byAdding: .hour, value: hourOffset, to: currentDate)!
let entry = SimpleEntry(date: entryDate)
entries.append(entry)
}
let timeline = Timeline(entries: entries, policy: .atEnd)
completion(timeline)
}
Notification is working fine
but one thing is pending, how can I call UNUserNotificationCenter.current().delegate = self?
Thanks

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")
}
})

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"