Local push notifications based on condition - Swift - swift

I'm trying to set an 'important events' repeated notification where users are notified to check my app based on specific dates that have an important event. So it only sends a notification if there is an important event today.
I have a global variable isImportant that is set as true.
Then I have a function with a switch statement that goes through specific dates and returns the important event, and if there are not important events (default), it makes isImportant = false
var isMainWater = true
func getForMain() -> Waterz {
let database = WaterzBank()
switch getHijriDateNumber() {
case "2-16":
return database.duaMakarem
case "2-17":
return database.duaFaraj
case "2-21":
return database.duaFaraj
default:
isMainWater = false
return getWaterzByDayOfWeek(database)
}
}
This is how I scheduled my local notification for this:
func scheduleImportantNotification() {
let content = UNMutableNotificationContent()
let item = getWaterForMain()
content.title = item.entitle ?? "Important Event"
content.body = "Don't forget to read \(item.entitle ?? "this") today!"
var dateComponents = DateComponents()
dateComponents.hour = 18
dateComponents.minute = 15
let trigger = UNCalendarNotificationTrigger(dateMatching: dateComponents, repeats: isMainWater)
let request = UNNotificationRequest(identifier: myNotificationID.ImportantNotifiationsID.rawValue, content: content, trigger: trigger)
notificationCenter.add(request)
}
So basically I tried repeats: isMainWater so that it would send a notification only if isMainWater = true. That didn't work. Even on days with no important event, I would still get a notification and it would be the same important event as the first time. It's not dynamic.
How would I achieve this?

Related

Repeating local notification after a given date

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!

How do I schedule a notification at a specific time, and then repeat it every x amount of time?

I am making a reminder app where you can schedule a reminder, that will then repeat every x seconds/minutes/hours/days etc.
If I want it to repeat every x amount of time I can do it like so:
func addNotification() {
let content = UNMutableNotificationContent()
content.title = "title"
// show this notification 5 minutes from now
var trigger: UNTimeIntervalNotificationTrigger
trigger = UNTimeIntervalNotificationTrigger(timeInterval: 300, repeats: true)
// choose a random identifier
let request = UNNotificationRequest(identifier: UUID().uuidString, content: content, trigger: trigger)
// add our notification request
UNUserNotificationCenter.current().add(request)
}
This is essentially what I want, but instead of starting 5 minutes from now, I want to be able to choose the start date and then have it repeat every 5 minutes seconds from that initial start date.
Is this possible?
From what I know it's not possible to repeat a notification every X seconds (or something else) after a specific date.
I think the "best" option here is to use UNCalendarNotificationTrigger instead and schedule 60/5 = 12 notification (so 1 every 5 seconds) starting from the given date.
Something like this:
// this is your reference date - here it's now + 5 seconds just for this example
var referenceDate = Calendar.current.date(byAdding: .second, value: 5, to: Date())!
for i in 0...11 { // one every 5 seconds, so total = 12
let content = UNMutableNotificationContent()
content.title = "Notif \(i)"
content.body = "Body"
var dateComponents = DateComponents(calendar: Calendar.current)
// 5 seconds interval here but you can set whatever you want, for hours, minutes, etc.
dateComponents.second = 5
//dateComponents.hour = X
// [...]
guard let nextTriggerDate = dateComponents.calendar?.date(byAdding: dateComponents, to: referenceDate),
let nextTriggerDateCompnents = dateComponents.calendar?.dateComponents([.second], from: nextTriggerDate) else {
return
}
referenceDate = nextTriggerDate
print(nextTriggerDate)
let trigger = UNCalendarNotificationTrigger(dateMatching: nextTriggerDateCompnents, repeats: true)
let request = UNNotificationRequest(identifier: "notif-\(i)", content: content, trigger: trigger)
UNUserNotificationCenter.current().add(request)
}
Now based on that, you would need to handle when a user taps one of the notifications in order to cancel all the others. But that's another topic here and I leave it up to you to find the logic for that.

UNUserNotification. Send local notification in period of time

My task is to set a period of time when the user receives notifications. So I know this period (10.00–11.00), interval (5 min) and can calculate the number of notifications (period/interval = 12). So the app adds 12 requests to UNUserNotificationCenter. In these conditions, everything works fine. But there is a problem:
An app can have only a limited number of scheduled notifications; the system keeps the soonest-firing 64 notifications (with automatically rescheduled notifications counting as a single notification) and discards the rest.
If the user sets a longer period (10 hours) the number of scheduled notifications = 120 and they are not firing.
What can I do about the 64 notifications limit?
Maybe there's another way to schedule notifications in a specified period of time?
My code:
let calendar = Calendar.current
let period = calendar.dateComponents([.minute], from: self.startTime, to: self.endTime)
var intPeriod: Int {
var intPeriod = period.minute!
if intPeriod < 0 {
intPeriod = 1440 + intPeriod
}
return intPeriod
}
let quantity = intPeriod / minutes
var increasingMinutes = 0
for _ in 0.. < quantity + 1 {
var increasingTrigger = calendar.date(byAdding: .minute, value: increasingMinutes, to: self.startTime)
let dc = calendar.dateComponents([.hour, .minute], from: increasingTrigger!)
let trigger = UNCalendarNotificationTrigger(dateMatching: dc, repeats: true)
let request = UNNotificationRequest(identifier: UUID().uuidString, content: content, trigger: trigger)
UNUserNotificationCenter.current().add(request) {
(error) in
print("Error \(String(describing: error))")
}
increasingMinutes += minutes
}
startTime, endTime - bindable vars from DatePicker
minutes - bindable from Stepper

Creating UILocalNotifications with CoreData attribute

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.

Repeat UserNotification every specific day of week for iOS 10

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.