Daily LocalNotification with Start & End Time - ios

I want a notification to repeat daily, but with the start time, end time & frequency(fixed) given.
Say, start time is 10am, endTime 6pm & frequency is every 3hrs, then it should trigger notification at 10am, 1pm, 4pm respectively.
Note:- the application has 3 other local notification (with different title & body), so need to differentiate the notifications as user can stop any of these notifications anytime.
Tried with DLLocalNotification but it didn't solve my problem.
DLNotificationScheduler().repeatsFromToDate (identifier: String, alertTitle: String, alertBody: String, fromDate: Date, toDate: Date, interval: Double, repeats: RepeatingInterval, category: String = " ", sound: String = " ")
Any help would be highly appreciated

For posting the local notification with repeat, you can use below code :
let content = UNMutableNotificationContent()
content.title = "Pizza Time!!"
content.body = "Monday is Pizza Day"
content.categoryIdentifier = "pizza.reminder.category"
//Date component trigger
var dateComponents = DateComponents()
//example for Gregorian calendar. Every Monday at 11:30AM
dateComponents.hour = 11
dateComponents.minute = 30
dateComponents.weekday = 2
// for testing, notification at the top of the minute.
dateComponents.second = 0
let trigger = UNCalendarNotificationTrigger(
dateMatching: dateComponents,
repeats: true)
let request = UNNotificationRequest(identifier: "pizza.reminder", content: content, trigger: trigger)
UNUserNotificationCenter.current().add(request) { (error) in
if let error = error {
print("error in pizza reminder: \(error.localizedDescription)")
}
}
You can add multiple types of notification by defining the different identifiers for each.
To Stop the local notification,
UNUserNotificationCenter.current().removePendingNotificationRequests(withIdentifiers: ["pizza.reminder"])
To get the notification of the local notifications which are still remaining to be posted, you can use below code
UNUserNotificationCenter.current().getPendingNotificationRequests {
(requests) in
displayString += "count:\(requests.count)\t"
for request in requests{
displayString += request.identifier + "\t"
}
print(displayString)
}
This things are sufficient enough to add some logic and meet your requirements.
For more information you can follow :
https://makeapppie.com/2017/01/31/how-to-repeat-local-notifications/

Related

Swift 4 How to get all events from calendar?

I am using Swift 4.1. And I want to write a function which will collect all events from all calendars in Calendar app of iOS. Thanks to this answer on stackoverflow: How to get all Events out of a Calendar (Swift) I was able to write my own class and call it Cale. Please, look at this:
import UIKit
import EventKit
class Cale {
private func createDate(year: Int) -> Date {
var components = DateComponents()
components.year = year
components.timeZone = TimeZone(secondsFromGMT: 0)
return Calendar.current.date(from: components)!
}
private let eventStore = EKEventStore()
private func get() {
let calendars = eventStore.calendars(for: .event)
for calendar in calendars {
// This checking will remove Birthdays and Hollidays callendars
guard calendar.allowsContentModifications else {
continue
}
let start = createDate(year: 2016)
let end = createDate(year: 2025)
print("start: \(start)")
print(" end: \(end)")
let predicate = eventStore.predicateForEvents(withStart: start, end: end, calendars: [calendar])
print("predicate: \(predicate)")
let events = eventStore.events(matching: predicate)
for event in events {
print(" title: \(event.title!)")
print("startDate: \(event.startDate!)")
print(" endDate: \(event.endDate!)")
}
}
}
func checkStatusAndGetAllEvents() {
let currentStatus = EKEventStore.authorizationStatus(for: EKEntityType.event)
switch currentStatus {
case .authorized:
//print("authorized")
self.get()
case .notDetermined:
//print("notDetermined")
eventStore.requestAccess(to: .event) { accessGranted, error in
if accessGranted {
self.get()
} else {
print("Change Settings to Allow Access")
}
}
case .restricted:
print("restricted")
case .denied:
print("denied")
}
}
}
Quite simple class, you can use it, it is working but with one exception.
The main function there is get() In this function I am creating predicate based on two dates: start and end. As you can see start date is:
2016-01-01 00:00:00 +0000
and end date is:
2025-01-01 00:00:00 +0000
But if we run the program we will see that predicate will be like this:
CADEventPredicate start:01/01/2016, 03:00; end:01/01/2020, 03:00;
cals:(
2 )
Only from 2016 to 2020, 4 years! I have tested it on different dates, but I could get predicate with 4 years interval maximum. It means, it will not give me all events! So question is: How to get all events from calendar? If it possible, without using dates!
Thank you for any future help or advice!
In case anyone is still wondering, Apple limited this to four years intentionally.
From predicateForEvents(withStart:end:calendars:):
For performance reasons, this method matches only those events within a four year time span. If the date range between startDate and endDate is greater than four years, it is shortened to the first four years.
If you want a range longer than that, I guess you'll have to wrap this in your own function to split it into four year chunks.
After spending so much time, i got a solution to my problem. My problem was as below,
event start date 2019-03-15 09:00:00 +0000
event end date 2019-03-15 14:00:00 +0000
the predicate as below,
CADEventPredicate start:15/03/19, 1:30 PM; end:15/03/19, 7:30 PM; cals:(null)
As i am from India, the predicate is adding GMT+5:30 to my actual event start and end time. I got a method from stack overflow, to convert to local & global timezone as below.
extension Date {
// Convert local time to UTC (or GMT)
func toGlobalTime() -> Date {
let timezone = TimeZone.current
let seconds = -TimeInterval(timezone.secondsFromGMT(for: self))
return Date(timeInterval: seconds, since: self)
}
// Convert UTC (or GMT) to local time
func toLocalTime() -> Date {
let timezone = TimeZone.current
let seconds = TimeInterval(timezone.secondsFromGMT(for: self))
return Date(timeInterval: seconds, since: self)
}
}
While passing start and end date with time to predicate i am converting to globalTimeZone as below.
let predicate = eventStore.predicateForEvents(withStart: eventStartDateTime.toGlobalTime(), end: eventEndDateTime.toGlobalTime(), calendars: nil)
If you don't have time, you have only start & end dates, then use predicate as below,
event start date 2019-03-15 00:00:00 +0000
event end date 2019-03-15 00:00:00 +0000
let predicate = eventStore.predicateForEvents(withStart: eventStartDateTime, end: eventEndDateTime, calendars: nil)
Because if you don't have time and converting to globalTimeZone, you may face issues in deleting the events.
I hope it will help some one.

Create weekly swift ios local notifications

I can create daily notifications but not weekly ones using swift 3 ios 10 User Notifications framework. Daily works fine but when I add in the .weekday parameter it doesn't send me a notification.
The code is:
for i in 1 ... 7
{
let center = UNUserNotificationCenter.current();
let content = UNMutableNotificationContent();
content.title = "Title";
content.body = "content";
content.sound = UNNotificationSound.default();
var dateComponents = DateComponents();
dateComponents.weekday = i;
// hours is 00 to 11 in string format
if (daynight == "am")
{
dateComponents.hour = Int(hours);
}
else
{
dateComponents.hour = Int(hours)! + 12;
}
// mins is 00 to 59 in string format
dateComponents.minute = Int(mins);
dateComponents.second = 0;
let date = Calendar.current.date(from: dateComponents);
// tiriggerDaily works without weekday for daily but adding weekday doesn't make it weekly
let triggerDaily = Calendar.current.dateComponents([.weekday, .hour, .minute, .second], from: date!);
let trigger = UNCalendarNotificationTrigger(dateMatching: triggerDaily, repeats: true);
let identifier = // a uuid;
let request = UNNotificationRequest(identifier: identifier, content: content, trigger: trigger);
center.add(request, withCompletionHandler:
{
(error) in
if let error = error
{
// Error happened
print("Error: ");
print(error);
}
});
Need to get code to figure out weekly. Used this as guidance: https://useyourloaf.com/blog/local-notifications-with-ios-10/
An example of what I want is this:
A user selects Monday, 11:25pm and it is a Monday at 11:23pm.
Then the user should get a notification in two minutes plus one the following week and so on.
Some other notes/questions:
1. Do you have to wait a week for weekly notifications to start? Like in the example above will it start the following week?
Just a thought since I can't get this working right yet.

How to check the amount of time between two NSDates?

I'm making a kind of challenge based app that requires that the user comes back every day. If he misses one day, he has to start all over again.
My problem is my dateChanged()-function; the first thing is, that it doesn't work very reliable, the second is that I just check if the date changed, I accordingly don't know if there were one or two days between using the app.
Here's my current function:
public func changeDays()
{
myFormatter.dateStyle = .short
myFormatter.locale = Locale(identifier: "de_DE")
oldDate = defaults.string(forKey: "oldDate")!
let newDate = Date()
let newDateString = myFormatter.string(from: newDate)
if newDateString == oldDate
{
NumberOfDaysInARow.text = "\(days) / 30"
}
else if newDateString != oldDate
{
days += 1
NumberOfDaysInARow.text = "\(days) / 30"
defaults.set(days, forKey: "days")
}
oldDate = newDateString
defaults.set(oldDate, forKey: "oldDate")
}
Just today it started giving me a fatal error when starting the app on my iPhone, did not happen in the simulator though... weird.
How do I have to change my function to make it a 100% reliable (and working) while also being able to check the amount of time between the two dates?
Thank you for having a look! Have a great day
You could extend Date with the function below that returns the amount of days from another date.
extension Date {
// Returns the amount of days from another date
func days(from date: Date) -> Int {
return Calendar.current.dateComponents([.day], from: date, to: self).day ?? 0
}
}
Instead of saving oldDate as a string you can set it to defaults as a date:
defaults.set(Date(), forKey: "oldDate")
You can get the old date from defaults using:
let oldDate = defaults.object(forKey:"oldDate") as! Date
Once you have your old date
let dateNow = Date()
let timeDifference = dateNow.days(from: oldDate!)
If timeDifference > 1 {
// older than 1 day
} else {
// Streak still alive
}
}
If you look in the documentation you will see that Date has a method whose sole purpose is too determine the interval between two dates time​Interval​Since(_:​).
If you set the old date to always be 11:59PM on the day it was last used you only have to see if the interval is greater than 24 hours worth of seconds (60 seconds * 60 minutes * 24 hours).
You may want to look at the docs for DateComponents for help creating a date that uses the current date but with a specific time.

How can i schedule local notification for Mon,Tue and Fri at 5PM in ios swift?

In swift 3, how can I schedule local notification for a particular day like Mon, Tue and Sat for a specific time like 5 PM?
Currently, I have schedule local notification for a time which I have set.
let myOwnDate = Date()
dateFormatter.dateFormat = "dd/MM/yyyy"
let currentDate = dateFormatter.string(from: myOwnDate)
let dateTime = currentDate + " " + self.btnTime.titleLabel!.text!
dateFormatter.dateFormat = "dd/MM/yyyy hh:mm a"
let date = dateFormatter.date(from: dateTime)!
localNotification.fireDate = date
localNotification.repeatInterval = NSCalendar.Unit.weekday
localNotification.alertBody = "Your alarm is ringing!"
let app = UIApplication.shared
app.scheduleLocalNotification(localNotification)
But the problem is that this notification fires for all days.
You should set the repeatInterval to
NSCalendar.Unit.weekOfYear
instead of weekday. Weekday will fire every day of the week.
You should also create 3 notifications like this, one for Monday, one for Tuesday and one for Friday. Each will repeat weekly.

Having trouble scheduling local notification to fire at specific time and date in swift

I am trying to schedule a notification at a certain time in swift. My TimeString is a string that looks like 08:32 or 12:23. When I try to fire on this specific date and time, the notification only fires on the specific date however not the specific time. For instance if I change the year to 2016 it won't fire. If I change the month it won't fire. Yet regardless of what time I put in, if the date matches the current date, I am writing this on 1/28/2015, the notification fires regardless of time, In fact it fires as soon as I close the application as my code is in
func applicationDidEnterBackground(application: UIApplication) {
}
My code does compile so I don't think there is an error in my code.
Is there any way to fire a notification at a specific time and not just a date in swift, and if so how do I implement this.
var timeString : String = "2015-01-28 " + TimeString;
println(timeString);
let formatter = NSDateFormatter()
formatter.dateFormat = "yyyy-MM-dd HH:mm"
formatter.timeZone = NSTimeZone(abbreviation: "GMT");
var date = formatter.dateFromString(timeString)
var localNotification:UILocalNotification = UILocalNotification()
localNotification.alertAction = "Block"
localNotification.alertBody = block.description() + " Started";
localNotification.fireDate = date
UIApplication.sharedApplication().scheduleLocalNotification(localNotification)
Try using this approach to set your date:
let calendar = NSCalendar.currentCalendar()
let calendarComponents = NSDateComponents()
calendarComponents.hour = 8 //you can get you time components
calendarComponents.second = 0 //from your string rather than
calendarComponents.minute = 0 //using fixed values
calendarComponents.day = 28
calendarComponents.year = 2015
calendarComponents.month = 1
let dateToFire = calendar.dateFromComponents(calendarComponents)
It worked for me. Hope this helps. :)

Resources