How to call function at 8:30am every day - ios

Im trying to make a quiz app and I want it to show a new question every day at 8:30am and disappear at 6pm. I have seen online that the main way is to use timer but my question is if the person changes time zones, won't the timer be out of sync? Or does it restart every day? here is my code: (please note I am a beginner)
Also, whenever I load my app, the question appears for a second and then disappears and then appears again after the timer is up.
#objc func loadQuestion() {
questionView.alpha = 0
self.view.addSubview(questionView)
questionView.center = self.view.center
questionView.transform = CGAffineTransform.init(scaleX: 1.3, y: 1.3)
UIView.animate(withDuration: 0.4) {
self.visualEffectView.effect = self.effect
self.questionView.alpha = 1
self.questionView.transform = CGAffineTransform.identity
let date = Date().addingTimeInterval(30600)
let timer = Timer(fireAt: date, interval: 0, target: self, selector: #selector(self.loadQuestion), userInfo: nil, repeats: false)
RunLoop.main.add(timer, forMode: .common)
}
}
#objc func loadOutQuestion() {
UIView.animate(withDuration: 0.3, animations: {
self.questionView.transform = CGAffineTransform.init(scaleX: 1.3, y: 1.3)
self.questionView.alpha = 0
self.visualEffectView.effect = nil
let date = Date().addingTimeInterval(64800)
let timer = Timer(fireAt: date, interval: 0, target: self, selector: #selector(self.loadOutQuestion), userInfo: nil, repeats: false)
RunLoop.main.add(timer, forMode: .common)
}) { (succcess:Bool) in
self.questionView.removeFromSuperview()
}
}

You can use "Local Notification I put apple sample code please check it and you can modify sample code as per your requirement.
The steps for configuring a local notification are as follows:
Create and configure a UNMutableNotificationContent object with the notification details.
Create a UNCalendarNotificationTrigger, UNTimeIntervalNotificationTrigger, or UNLocationNotificationTrigger object to describe the conditions under which the notification is delivered.
Create a UNNotificationRequest object with the content and trigger information.
Call the addNotificationRequest:withCompletionHandler: method to schedule the notification; see Scheduling Local Notifications for Delivery
Official Document
Sample code
let content = UNMutableNotificationContent()
// Replace "Wake up!" with your "question title"
content.title = NSString.localizedUserNotificationString(forKey: "Wake up!", arguments: nil)
// Replace "Rise and shine! It's morning time!" with your "question description"
content.body = NSString.localizedUserNotificationString(forKey: "Rise and shine! It's morning time!",
arguments: nil)
// Configure the trigger for a 7am wakeup.
var dateInfo = DateComponents()
dateInfo.hour = 7
dateInfo.minute = 0
let trigger = UNCalendarNotificationTrigger(dateMatching: dateInfo, repeats: false)
// Create the request object.
let request = UNNotificationRequest(identifier: "MorningAlarm", content: content, trigger: trigger)

This is what I have so far. I don't know if Im getting closer or farther from resolving this issue
#objc func loadQuestion() {
self.view.addSubview(questionView)
questionView.center = self.view.center
questionView.transform = CGAffineTransform.init(scaleX: 1.3, y: 1.3)
questionView.alpha = 0
var dateComponents = DateComponents()
dateComponents.hour = 08
dateComponents.minute = 30
let currentTime = DateComponents()
let hour = currentTime.hour
let minute = currentTime.minute
var calendar = Calendar.current
calendar.timeZone = NSTimeZone.local
if hour >= dateComponents {
UIView.animate(withDuration: 0.4) {
self.visualEffectView.effect = self.effect
self.questionView.alpha = 1
self.questionView.transform = CGAffineTransform.identity
}
}
}

This will get 8:30am and 6:00pm from user's local time.
var calendar = Calendar.current
calendar.timeZone = NSTimeZone.local
let startDate = calendar.startOfDay(for: Date())
let EightAM = calendar.date(byAdding: .hour, value: 8, to: startDate)!
let EightThirtyAM = calendar.date(byAdding: .hour, value: 30, to: EightAM)!
let SixPM = calendar.date(byAdding: .hour, value: 18, to: startDate)!
If the App isn't open at these times, nothing will fire.
What you could do in this case is in ViewDidLoad, check
if Date() > EightThirtyAM && Date() < SixPM {
showQuiz()
} else {
hideQuiz()
}
If you want it to disappear at 6:00pm on the dot add
let timer = Timer(fireAt: SixPM, interval: 0, target: self, selector: #selector(hideQuiz), userInfo: nil, repeats: false)
RunLoop.main.add(timer, forMode: .common)
If you prefer a component solution try.
var calendar = Calendar.current
calendar.timeZone = NSTimeZone.local
let hours = calendar.component(.hour, from: Date())
let minutes = calendar.component(.minute, from: Date())
let timeFloat:Float = Float(hours) + Float(minutes)/60
if timeFloat > 8.5 && timeFloat < 18 {
showQuiz
} else {
hideQuiz
}

Related

How to schedule a local notification two specific days of the week?

I want to schedule a notification to be fired twice a week:
let gregorian = Calendar(identifier: .gregorian)
let now = Date()
[2, 4].forEach { day in
let content = UNMutableNotificationContent()
content.title = "Notif title"
content.body = "Comment"
content.sound = .default
var components = gregorian.dateComponents([.year, .month, .weekdayOrdinal, .day, .hour, .minute, .second], from: now)
components.hour = 11
components.minute = 25
components.second = 0
components.weekdayOrdinal = day
let date = gregorian.date(from: components)!
let triggerDaily = gregorian.dateComponents([.weekdayOrdinal, .hour, .minute, .second], from: date)
let trigger = UNCalendarNotificationTrigger(dateMatching: triggerDaily, repeats: true)
let request = UNNotificationRequest(identifier: "test", content: content, trigger: trigger)
UNUserNotificationCenter.current().add(request) { error in
if let error = error {
print("Oops: \(error)")
} else {
print("Notification created!")
}
}
}
But instead of being fired once on monday and once on wednesday, it gets called two times each day.
What am I doing wrong? Thanks for your help.
because of foreach loop your code schedule two notification with the same identifier. while your code is getting execute the 2nd loop cycle overwrite the request.
I did some R&D and refine your code a bit. On my side, it works perfectly.
Please try the code mention below.
func setNotification() {
let days = [2,4]
func ScheduleNotification(index:Int){
let calendarComponents = NSDateComponents()
calendarComponents.hour = 11
calendarComponents.minute = 25
calendarComponents.second = 0
calendarComponents.weekday = days[index]
let content = UNMutableNotificationContent()
content.title = "Notification title"
content.body = "your message here."
content.sound = UNNotificationSound.default
let trigger = UNCalendarNotificationTrigger(dateMatching: calendarComponents as DateComponents, repeats: true)
let identifier = "UYLLocalNotification\(days[index])"
let request = UNNotificationRequest(identifier: identifier, content: content, trigger: trigger)
UNUserNotificationCenter.current().add(request, withCompletionHandler: { (error) in
if let error = error {
print(error)
}else{
if index == 0{
ScheduleNotification(index: 1)
}
}
})
}
ScheduleNotification(index: 0)
}
call this function in your viewdidLoad method like.
self.setNotification()
i hope it will save your time.
Simply change the name of identifier of each notification inside the foreach(_:) to something like test2, test4, i.e.
[2, 4].forEach {
let content = UNMutableNotificationContent()
content.title = "title"
content.body = "body"
content.sound = .default
var components = DateComponents()
components.timeZone = TimeZone.current
components.weekday = 11
components.hour = 25
components.minute = 0
components.second = $0
let trigger = UNCalendarNotificationTrigger(dateMatching: components, repeats: true)
let request = UNNotificationRequest(identifier: "test\($0)", content: content, trigger: trigger)
UNUserNotificationCenter.current().add(request) { error in
if let error = error {
print("Oops: \(error)")
} else {
print("Notification created!")
}
}
}
You can use .weekday instead of .weekdayOrdinal . According to Apple document
.weekday = A weekday or count of weekdays.
.weekdayOrdinal = weekday ordinal or count of weekday ordinals.
Weekday ordinal units represent the position of the weekday within the
next larger calendar unit, such as the month. For example, 2 is the
weekday ordinal unit for the second Friday of the month.
Try this
let triggerDate = Calendar.current.dateComponents([.weekday,.hour,.minute], from: date as Date)
triggerDat.weekday = day[index]
let trigger = UNCalendarNotificationTrigger(dateMatching: triggerDate,
repeats: true)

How do I setup local notification repeated each 7, 14, 28 days from specific day

I need to push local notification each 7, 14, 28 days.
I had successfully setup to push weekly (7 days)
I am not fully understand how DateComponents works.
IE: I need to repeat notification each minute, I do Calendar.current.dateComponents([.second], from: fireDate) How does it work with repeated 14 days, 28 days.
// datetime is timestamp which I can choose: ie: May 02, 2029
let fireDate = Date(timeIntervalSince1970: Double(truncating: datetime) / 1000)
let fireDateComps = Calendar.current.dateComponents([.year, .month, .day, .hour, .minute, .second], from: fireDate)
let content = UNMutableNotificationContent()
content.title = title
content.body = note
content.sound = UNNotificationSound.default
content.categoryIdentifier = "timer.category"
var triggerDate = Calendar.current.dateComponents([.year, .month, .day, .hour, .minute, .second], from: fireDate)
var repeats = false
switch (recurring) {
case "weekly":
triggerDate = Calendar.current.dateComponents([ .weekday , .hour , .minute , .second], from: fireDate)
repeats = true
print("reminder repeat weekly")
case "every_two_weeks":
print("triggerDate 1 \(fireDate)")
let aaa = Calendar.current.date(byAdding: .day, value: 14, to: fireDate)
print("triggerDate 2 \(aaa)")
triggerDate = Calendar.current.dateComponents([.day, .hour, .minute, .second], from: aaa!)
print("triggerDate 3 \(triggerDate)")
repeats = true
print("reminder repeat every_two_weeks")
case "every_four_weeks":
triggerDate = Calendar.current.dateComponents([ .month , .hour , .minute , .second], from: fireDate)
repeats = true
print("reminder repeat every_four_weeks")
default:
triggerDate = Calendar.current.dateComponents([ .year, .month, .day , .hour , .minute , .second], from: fireDate)
repeats = false
print("No recurring")
}
let trigger = UNCalendarNotificationTrigger(dateMatching: triggerDate, repeats: repeats)
print("trigger \(trigger), firstRepeatingDate \(triggerDate)")
let request = UNNotificationRequest(identifier: id,
content: content, trigger: trigger)
self.center.add(request) {(error) in
if let error = error {
Log.nonFatal("Save notification failed \(String(describing: error))")
reject("reminder", "Save reminder notification failed", error)
}
print("Saved recurring notification")
resolve(true)
}
It's been a while since I have used these apis, but based on the documentation here is a quick example for a couple of them (the one you use and the one I am proposing):
// UNCalendarNotificationTrigger (Trigger at specific date & time)
// This example fires a notification every day at 08:30
var date = DateComponents()
date.hour = 8
date.minute = 30
let trigger = UNCalendarNotificationTrigger(dateMatching: date, repeats: true)
/* I guess date.day = 1 would fire at the 1st day of each month
whereas date.weekOfMonth = 2 would fire at the second week of each month etc
*/
What you may want instead is to fire periodically:
// UNTimeIntervalNotificationTrigger (Trigger after an amount of time has elapsed)
// This example fires a notification every 14 days (14d * 24h * 60m * 60s)
let days = 14
let trigger = UNTimeIntervalNotificationTrigger(timeInterval: TimeInterval(days * 24 * 60 * 60), repeats: true)
I had resolved the question.
func calendarAddNotification(id: String, title: String, datetime: NSNumber, note: String, recurring: String) {
let fireDate = Date(timeIntervalSince1970: Double(truncating: datetime) / 1000)
let content = UNMutableNotificationContent()
content.title = title
content.body = note
content.sound = UNNotificationSound.default
content.categoryIdentifier = "timer.category"
// setup normal notification
let triggerDate = Calendar.current.dateComponents([.year, .month, .day, .hour, .minute, .second], from: fireDate)
let trigger = UNCalendarNotificationTrigger(dateMatching: triggerDate, repeats: false)
let request = UNNotificationRequest(identifier: id,
content: content, trigger: trigger)
self.center.add(request) {(error) in
if let error = error {
Log.nonFatal("Save notification failed \(String(describing: error))")
} else {
print("Saved notification")
}
}
// non-recurring notification, return
if (!["weekly", "every_two_weeks", "every_four_weeks"].contains(recurring)) {
print("No recurring notification")
return
}
// recurring notification
var interval = 0
switch (recurring) {
case "weekly":
interval = 7
print("reminder repeat weekly")
case "every_two_weeks":
interval = 14
print("reminder repeat every_two_weeks")
case "every_four_weeks":
interval = 28
print("reminder repeat every_four_weeks")
default:
print("No recurring")
}
// add interval day to the fireDate
let recurringDateComponent = Calendar.current.date(byAdding: .day, value: interval, to: fireDate)
let recurringTrigger = UNTimeIntervalNotificationTrigger(timeInterval: recurringDateComponent!.timeIntervalSinceNow, repeats: true)
let recurringRequest = UNNotificationRequest(identifier: "recurring_" + id,
content: content, trigger: recurringTrigger)
self.center.add(recurringRequest) { (error) in
if let error = error {
Log.nonFatal("Save recurring notification failed \(String(describing: error))")
} else {
print("Saved recurring notification")
}
}
}

Triggering a weekly notification in swift 3 using local notifications api

I want to trigger a weekly notification that goes on Mondays at 7:00pm.
I see code like this:
let triggerWeekly = Calendar.current.dateComponents([.weekday, .hour, .minute, .second], from: date)
let trigger = UNCalendarNotificationTrigger(dateMatching: triggerWeekly, repeats: true)
What should I set the from date as?
Also does the date need to be in the present?
I am using iOS 10+ and swift 3.
Pass date which you want to generate local notification
let strDate = (data as AnyObject).value(forKey: "not_date") as? String
let dateformatter = DateFormatter()
dateformatter.dateFormat = "yyyy-MM-dd hh:mm a"
let notidate = dateformatter.date(from: strDate!)
you need to first stattnotificationdate like = 2018-06-25 07:00 PM after you need to add time interval
var dateStart = Date()
dateStart = dateStart.addingTimeInterval(TimeInterval(604800))
and generate notification with these interwal
//MARK:- Schedule Notification with Daily bases.
func scheduleNotification(at date: Date, body: String, titles:String) {
let triggerDaily = Calendar.current.dateComponents([.year, .month, .day, .hour, .minute, .second], from: date)
if #available(iOS 10.0, *) {
let trigger = UNCalendarNotificationTrigger(dateMatching: triggerDaily, repeats: true)
let content = UNMutableNotificationContent()
content.title = titles
content.body = body
content.sound = UNNotificationSound.default()
content.categoryIdentifier = "HardikBar"
let request = UNNotificationRequest(identifier: "NotificationAt-\(date))", content: content, trigger: trigger)
UNUserNotificationCenter.current().add(request) {(error) in
if let error = error {
print("Uh oh! We had an error: \(error)")
}
}
} else {
// Fallback on earlier versions
}
}
I have used below extension for Date:
extension Date {
static func today() -> Date {
return Date()
}
func next(_ weekday: Weekday, considerToday: Bool = false) -> Date {
return get(.Next,
weekday,
considerToday: considerToday)
}
func previous(_ weekday: Weekday, considerToday: Bool = false) -> Date {
return get(.Previous,
weekday,
considerToday: considerToday)
}
func get(_ direction: SearchDirection,
_ weekDay: Weekday,
considerToday consider: Bool = false) -> Date {
let dayName = weekDay.rawValue
let weekdaysName = getWeekDaysInEnglish().map { $0.lowercased() }
assert(weekdaysName.contains(dayName), "weekday symbol should be in form \(weekdaysName)")
let searchWeekdayIndex = weekdaysName.index(of: dayName)! + 1
let calendar = Calendar(identifier: .gregorian)
if consider && calendar.component(.weekday, from: self) == searchWeekdayIndex {
return self
}
var nextDateComponent = DateComponents()
nextDateComponent.weekday = searchWeekdayIndex
let date = calendar.nextDate(after: self,
matching: nextDateComponent,
matchingPolicy: .nextTime,
direction: direction.calendarSearchDirection)
return date!
}
}
This can be used for getting any next or previous weekday from the reference date. You'll need to get the next Monday first and then set the UNCalendarNotificationTrigger as below:
var triggerDaily = Calendar.current.dateComponents([.year, .month, .day, .hour, .minute, .second], from: Date().next(Date.Weekday.monday, considerToday: true))
triggerDaily.hour = 19
triggerDaily.minute = 0
triggerDaily.second = 0
if #available(iOS 10.0, *) {
let trigger = UNCalendarNotificationTrigger(dateMatching: triggerDaily, repeats: true)
let content = UNMutableNotificationContent()
content.title = "Title"
content.body = "Body"
content.sound = UNNotificationSound.default()
content.categoryIdentifier = "Test"
let request = UNNotificationRequest(identifier: "NotificationAt-\(String(describing: Calendar.current.date(from: triggerDaily))))", content: content, trigger: trigger)
UNUserNotificationCenter.current().add(request) {(error) in
if let error = error {
print("Uh oh! We had an error: \(error)")
}else {
UNUserNotificationCenter.current().getPendingNotificationRequests(completionHandler: { (arrayRequests) in
for request in arrayRequests {
if let calendarNotificationTrigger = request.trigger as? UNCalendarNotificationTrigger,
let nextTriggerDate = calendarNotificationTrigger.nextTriggerDate() {
print("nextTriggerDate ===>>> \(nextTriggerDate)")
}
print("Request ===>>> \(String(describing: request.trigger))")
}
})
}
}
} else {
// Fallback on earlier versions
}
The Output of this will be as below:
nextTriggerDate ===>>> 2018-07-02 13:30:00 +0000
Request ===>>> Optional(
Calendar Year: 2018
Month: 7
Day: 2
Hour: 19
Minute: 0
Second: 0, repeats: YES>)
You can set weekday and time in DateComponents you can receive notification for every monday at 7:00 PM and also set Repeat = "true"
func setupNotificationReminder() {
let title:String = "Your reminder text goes here"
var calendarComponents = DateComponents()
calendarComponents.weekday = 2
calendarComponents.hour = 19
calendarComponents.second = 0
calendarComponents.minute = 0
// create a corresponding local notification
let trigger = UNCalendarNotificationTrigger(dateMatching: calendarComponents, repeats: true)
let content = UNMutableNotificationContent()
content.title = title
content.body = "body"
content.sound = UNNotificationSound.default()
content.categoryIdentifier = "YourApp"
let request = UNNotificationRequest(identifier: "NotificationAtGivenTime", content: content, trigger: trigger)
UNUserNotificationCenter.current().add(request) {(error) in
if let error = error {
print("Uh oh! We had an error: \(error)")
}
}
}
Another overload of trigger required TimeInterval
So you can get
let timeInterval = dateOfNotification.timeIntervalSinceNow
Now
let trigger = UNTimeIntervalNotificationTrigger(timeInterval: timeInterval, repeats: true)
You can create date as
if let date = Calendar.current.date(byAdding: .day, value: 7, to: self) {
let date1 = Calendar.current.date(bySettingHour: hours, minute: minutes, second: 00, of: date)
}
If you want specific day then refer https://stackoverflow.com/a/49561764/4601900

How to set the time of start and end of the repeat local notification?

I tried to repeat a notification with UNCalendarNotificationTrigger(dateMatching:, repeats:), but this method can only repeat at a certain time.
I also tried UNTimeIntervalNotificationTrigger(timeInterval:, repeats:) and repeated a notification by a time interval, but this method could not set the start time of push notification.
And these two methods seems that there is no place to set a time for end push notifications.
I want to start with a special time and repeat notifications at regular intervals. What should I do?
Rather than using the repeats parameter you could loop from your start time to end time scheduling individual notifications.
let notifIDPrefix = "mynotif"
let notifCategory = "com.mydomain.mynotif" // this should have been registered with UNUserNotificationCenter
func scheduleNotifs(from startDate: Date, to endDate: Date, with interval: TimeInterval) {
var curDate = startDate
var count: Int = 0
while curDate.compare(endDate) != .orderedDescending {
scheduleNotif(with: "\(notifIDPrefix)_\(count)", date: curDate)
curDate = curDate.addingTimeInterval(interval)
count += 1
}
}
private func scheduleNotif(with identifier: String, date: Date) {
let content = UNMutableNotificationContent()
content.title = "My Title"
content.body = " "
content.categoryIdentifier = notifCategory
content.sound = UNNotificationSound.default()
let triggerTime = Calendar.current.dateComponents([.year, .day, .hour, .minute, .second], from: date)
let trigger = UNCalendarNotificationTrigger(dateMatching: triggerTime, repeats: false)
let request = UNNotificationRequest(identifier: identifier, content: content, trigger: trigger)
let center = UNUserNotificationCenter.current()
center.add(request) { (error : Error?) in
if let theError = error {
print(theError.localizedDescription)
}
}
}
The following would schedule 3 notifications (1, 2, and 3 minutes from now).
let startDate = Date().addingTimeInterval(60)
let endDate = startDate.addingTimeInterval(60 * 2)
let interval: TimeInterval = 60
scheduleNotifs(from: startDate, to: endDate, with: interval)

How to repeat a UNUserNotification every day?

I'm trying to allow the user to schedule notifications to open the app at a certain time every day. So far, I've been able to schedule the first notification by calculating the time between now and when the user selects, and scheduling a notification in X seconds. However, is there a way that I can then set that notification to repeat every day? Here's part of my code in case you're confused:
let newTime: Double = Double(totalDifference)
let notifTrigger = UNTimeIntervalNotificationTrigger(timeInterval: newTime, repeats: false)
let request = UNNotificationRequest(identifier: "openApp", content: notif, trigger: notifTrigger)
UNUserNotificationCenter.current().add(request, withCompletionHandler: { error in
if error != nil {
print(error!)
completion(false)
} else {
completion(true)
}
})
Any help is much appreciated
In your case, change this line:
let notifTrigger = UNTimeIntervalNotificationTrigger(timeInterval: newTime, repeats: false)
to
let notifTrigger = UNTimeIntervalNotificationTrigger(timeInterval: newTime, repeats: true)
From the Documentation:
UNCalendarNotificationTrigger:
Triggers a notification at the specified date and time. You use a
UNCalendarNotificationTrigger object to specify the temporal
information for the trigger condition of a notification. Calendar
triggers can fire once or they can fire multiple times.
NSDateComponents* date = [[NSDateComponents alloc] init];
date.hour = 8;
date.minute = 30;
UNCalendarNotificationTrigger* trigger = [UNCalendarNotificationTrigger
triggerWithDateMatchingComponents:date repeats:YES];
Swift:
var date = DateComponents()
date.hour = 8
date.minute = 30
let trigger = UNCalendarNotificationTrigger(dateMatching: date, repeats: true)
FOR SWIFT3
let calendar = Calendar(identifier: .gregorian)
let components = calendar.dateComponents(in: .current, from: date)
let newComponents = DateComponents(calendar: calendar, timeZone: .current, month: components.month, day: components.day, hour: components.hour, minute: components.minute)
let trigger = UNCalendarNotificationTrigger(dateMatching: newComponents, repeats: true)

Resources