I am pretty new to swift, so I am just hoping someone could help me out - I am trying to use UIEventKit to add an event to the calendar (which I have figured out how to do) however there are two things that I am unsure about:
1 - I want to be able to add the event to the next instance of a particular day. eg. the next coming Monday
2 - I want to be able to have that event recurring on that day a certain number of times - lets say 4.
Any help anyone can offer on either of the above would be greatly appreciated!
Here's what i have so far:
func createEvent(eventStore: EKEventStore, title: String, startDate: NSDate, endDate: NSDate){
let event = EKEvent(eventStore: eventStore)
event.title = title
event.startDate = startDate
event.endDate = endDate
event.calendar = eventStore.defaultCalendarForNewEvents
do {
try eventStore.saveEvent(event, span: .ThisEvent)
}catch {
let alertView = UIAlertController(title: "Access Denied", message: "Please change your settings to allow us to access your calendar", preferredStyle: UIAlertControllerStyle.Alert)
alertView.addAction(UIAlertAction(title: "OK", style: UIAlertActionStyle.Default, handler: nil))
self.presentViewController(alertView, animated: true, completion: nil)
}
}
#IBAction func addToCalendar(sender: UIButton) {
let eventStore = EKEventStore()
let startDate = NSDate()
let endDate = startDate.dateByAddingTimeInterval(60 * 30)
if (EKEventStore.authorizationStatusForEntityType(.Event) != EKAuthorizationStatus.Authorized) {
eventStore.requestAccessToEntityType(.Event, completion: {
granted, error in
self.createEvent(eventStore, title: "Psychology Lecture", startDate: startDate, endDate: endDate)
})
}else {
createEvent(eventStore, title: "Psychology Lecture", startDate: startDate, endDate: endDate)
}
}
I want to be able to add the event to the next instance of a particular day. eg. the next coming Monday
How are you handling the UI for the app? A best option for this kind of case would be to display the Date Picker - and let the user choose a date. And you can then create an event for that specific date.
I want to be able to have that event recurring on that day a certain number of times - lets say 4.
Here's the documentation for creating Recurring Events and it seems you cannot have an event recurring n times on a particular day.
I would suggest - creating the event with a different time or creating it an all day event.
For an all day event
event.allDay = true
For creating the event with a different time - I bumped up the day with different time
createEvent(eventStore, title: "Psychology Lecture", startDate: startDate, endDate: endDate)
//Bump up the time by 30 minutes
startDate = startDate.dateByAddingTimeInterval(60 * 30)
endDate = endDate.dateByAddingTimeInterval(60 * 30)
self.createEvent(eventStore, title: "Another Psychology Lecture", startDate: startDate, endDate: endDate)
Does this help?
Related
I need to create different types of notifications. I managed to create a notification at a specific date, but now I need to create one that repeats daily, after a start date. It's basically a reminder to take a daily medication, but the user will only start taking that medication at a specific day.
Here is my func:
func addNotification(text: String, date: Date, id: String) {
let content = UNMutableNotificationContent()
content.title = "My App"
content.body = text
content.sound = .default
content.badge = NSNumber(integerLiteral: UIApplication.shared.applicationIconBadgeNumber + 1)
var dateComponents = DateComponents()
dateComponents.hour = Calendar.current.component(.hour, from: date)
dateComponents.minute = Calendar.current.component(.minute, from: date)
var trigger = UNCalendarNotificationTrigger(dateMatching: dateComponents, repeats: true)
let request = UNNotificationRequest(identifier: id, content: content, trigger: trigger)
UNUserNotificationCenter.current().add(request) { error in
if let error {
print(error.localizedDescription)
}
}
}
As it is, it will notify daily, but from the moment the func is fired. I need to receive a start date and only begin firing after that.
One thing that comes to mind is: if I also specify the year, month and day on "dayComponents" like this:
dateComponents.hour = Calendar.current.component(.hour, from: date)
dateComponents.minute = Calendar.current.component(.minute, from: date)
dateComponents.day = Calendar.current.component(.day, from: date)
dateComponents.month = Calendar.current.component(.month, from: date)
dateComponents.year = Calendar.current.component(.year, from: date)
and set the trigger to repeat, would it consider all the parameters (hour, minute, day, month, year) and never repeat (as this day will never happen again), or will it only consider hour and minute to repeat? I wanted to try that, but then I'd need to wait for a whole day, so maybe someone here knows the answer!
I have a list of EKEvent's for the current day, and I want to filter out any that have been declined by the current user so that the list only contains
How can I do this? I can't find any examples anywhere, or any property on the event itself to store this info.
Here's some sample code, if that helps:
let eventStore = EKEventStore()
let today = Date()
let tomorrow = Calendar.current.date(byAdding: .day, value: 1, to: Date())!
let predicate = self.eventStore.predicateForEvents(withStart: today, end: tomorrow, calendars: nil)
let events = self.eventStore.events(matching: predicate)
// #TODO: Filter events by removing declined events.
you can get the infos via attendees and then participantStatus -> declined
In my iOS app I am trying to create a localNotification to notify the user 15 minutes prior to the event beginning. However I am stuck. I am using CoreData to store data. I have an Appointment object which can be created. A date attribute is associated with a Appointment object. I am really stuck with it. I do not know how to set up the timeInterval and the rest of the notification process.
I do not know how to set up the timeInterval from the time the Appointment is created to 15 minutes prior to when it begins.
Here is some of my code:
func scheduleNotifications() {
let content = UNMutableNotificationContent()
guard let client = client, let name = client.name, let formula = formula, let date = formula.date else { return }
content.title = "BookMe"
content.subtitle = ""
content.body = "Your appointment with \(name) will begin soon."
content.badge = 1
let trigger = UNTimeIntervalNotificationTrigger(timeInterval: ??, repeats: false)
Edited: This is what I have but nothing is firing.
let date = formula.date
let fireDate = Calendar.current.date(byAdding: DateComponents(minute: -15), to: date as Date)
guard let timeInterval = fireDate?.timeIntervalSince(Date()) else { return }
let trigger = UNTimeIntervalNotificationTrigger(timeInterval: timeInterval, repeats: false)
let request = UNNotificationRequest(identifier: self.timerUserNotificationIdentifier, content: content, trigger: trigger)
UNUserNotificationCenter.current().add(request, withCompletionHandler: nil)
As I understand it you are looking to find a time interval between right now and 15 minutes before some date so that you can fire a notification 15 minutes before that date.
Here's a quick example I knocked up in a playground
// Create a date in the future - this is what you get from your own data object and I'm creating it here just so I have a date.
let scheduledDate = Calendar.current.date(from: DateComponents(year: 2017, month: 09, day: 22, hour: 22))!
// Create a date 15 minutes earlier than your shcheduled date, this is when you want your notification to fire
let fireDate = Calendar.current.date(byAdding: DateComponents(minute: -15), to: scheduledDate)!
// Then just work out the time interval between the fire date and now to get the time interval
let timeInterval = fireDate.timeIntervalSince(Date())
Excuse the force unwrapping of the created dates, these are because it's an example, you should instead not use exclamation marks and handle errors gracefully.
edited to add
UNTimeIntervalNotificationTrigger, which you are trying to use requires a TimeInterval between now and the time you want to fire the notification. A TimeInterval is a Double that represents a number of seconds. In some cases, such as this one, it represents a delay, the number of seconds between now and the time you want to fire the the notification. In other cases it represents a date by the number of seconds from a fixed date. This fixed date is either timeIntervalSince1970 - "The interval between the date object and 00:00:00 UTC on 1 January 1970." Which is what you use for UNIX timestamps or timeIntervalSinceReferenceDate - "The interval between the date object and 00:00:00 UTC on 1 January 2001."
Whatever you do, resist the temptation to modify dates by adding or removing numbers of seconds directly, use DateComponents instead.
I'm working on local notification scheduling module for iOS 10 which repeats local notification for example every Sunday or every Monday..etc. Lets say i scheduled a notification for this date which is 2016-12-27 10:53:22 +0000 and using UNCalendarNotificationTrigger with repeat value equals true, the notification get triggered for ones in that date, and it doesn't repeat next week at the same time.
What could be the reason for that? and how is it possible to repeat every week for specific day in iOS 10.
Here is the code for creating local notification:
let content = UNMutableNotificationContent()
content.title = object.title
content.body = object.body
content.sound = UNNotificationSound.default()
let date = object.fireDate
let triggerDate = Calendar.current.dateComponents([.year,.month,.day,.hour,.minute,.second,], from: date as Date)
let trigger = UNCalendarNotificationTrigger(dateMatching: triggerDate,
repeats: true)
// Swift
let identifier = object.id
let request = UNNotificationRequest(identifier: identifier,
content: content, trigger: trigger)
localNotification.add(request, withCompletionHandler: { (error) in
if error != nil {
// Something went wrong
print(error!)
}else {
print("Reminder \(object.id) has been added successfully at \(object.fireDate)")
}
})
Update:
I have also discovered after the notification get fired at that date and to check that there is no more pending notification exist or to check if it has been rescheduled again or not. actually with repeat equals true, it has not been scheduled again for next week.
UNUserNotificationCenter.current().getPendingNotificationRequests(completionHandler: { (notficiations) in
for localNotification in notficiations {
print(localNotification)
}
})
And the result was:
<UNNotificationRequest: 0x174223ca0; identifier: A1, content: <UNNotificationContent: 0x1742e2980; title: My Title, subtitle: (null), body: My Body, categoryIdentifier: , launchImageName: , peopleIdentifiers: (
), threadIdentifier: , attachments: (
), badge: (null), sound: <UNNotificationSound: 0x1740b1820>, hasDefaultAction: YES, shouldAddToNotificationsList: YES, shouldAlwaysAlertWhileAppIsForeground: NO, shouldLockDevice: NO, shouldPauseMedia: NO, isSnoozeable: NO, fromSnooze: NO, darwinNotificationName: (null), darwinSnoozedNotificationName: (null), trigger: <UNCalendarNotificationTrigger: 0x174223cc0; dateComponents: <NSDateComponents: 0x17415e140>
Calendar Year: 2016
Month: 12
Day: 27
Hour: 14
Minute: 46
Second: 15, repeats: YES>>
I don't know if its actually a bug in iOS or not.
Triggering date format is not proper to repeat notification in a day of a week.
Your current trigger date components includes year,month,day, etc so this notification repeat in each year in that particular month and day.Change trigger date like mentioned below to repeat notification in a day of a week.
let triggerDate = Calendar.current.dateComponents([.weekday,.hour,.minute], from: date as Date)
Here is approach which should work:
func addNotificationForAlarm(alarm: MyAlarm) {
let myAlarmNotifContent = UNMutableNotificationContent()
myAlarmNotifContent.title = "Reminder"
myAlarmNotifContent.body = alarm.activity
myAlarmNotifContent.sound = UNNotificationSound.default()
if alarm.repeatDays.count == 1 {
} else {
for index in 1...alarm.repeatDays.count {
createNotif(date: alarm.date, weekDay: index, content: myAlarmNotifContent)
}
}
}
private func createNotif(date: Date, weekDay: Int, content: UNNotificationContent) {
var dateComponent = DateComponents()
dateComponent.weekday = weekDay
dateComponent.hour = Calendar.current.dateComponents([.hour], from: date).hashValue
dateComponent.minute = Calendar.current.dateComponents([.minute], from: date).hashValue
let myAlarmTrigger = UNCalendarNotificationTrigger(dateMatching: dateComponent, repeats: true)
setupNotificationSettings()
let identifier = "Your-Notification"
let request = UNNotificationRequest(identifier: identifier, content: content, trigger: myAlarmTrigger)
let center = UNUserNotificationCenter.current()
center.add(request, withCompletionHandler: { (error) in
if error != nil {
//TODO: Handle the error
}
})
}
Basically what I have found is that you can set a separate notification for each day you want the alarm to trigger. For example, you want every Monday and every Tuesday, so create a date component for each day and add it to the trigger. It is not perfect but is a solution that I think is better then calculating time intervals.
I need to update my watchOS complication at midnight every day.
startOfDay is the beginning of the day (i.e., 12 AM today).
Should I add a day to the start of today like this?
func getNextRequestedUpdateDateWithHandler(handler: (NSDate?) -> Void) {
// Call the handler with the date when you would next like to be given the opportunity to update your complication content
let startOfDay = NSDate().startOfDay
let components = NSDateComponents()
components.day = 1
let startOfNextDay = NSCalendar.currentCalendar().dateByAddingComponents(components, toDate: startOfDay, options: NSCalendarOptions())
handler(startOfNextDay)
}
Or should I not add a day to the code, and just do something like this:
func getNextRequestedUpdateDateWithHandler(handler: (NSDate?) -> Void) {
// Call the handler with the date when you would next like to be given the opportunity to update your complication content
let startOfDay = NSDate().startOfDay
handler(startOfDay)
}
You'd want to advance the date one day, since you want your next requested update to occur at tomorrow's midnight. The first method would do what you want, but you can simplify it as follows:
let calendar = NSCalendar.currentCalendar()
let startOfDay = calendar.startOfDayForDate(NSDate())
let startOfNextDay = calendar.dateByAddingUnit(.Day, value: 1, toDate: startOfDay, options: NSCalendarOptions())!
The second code would return today's 12 AM, which would already be in the past.