iOS local notification base on day, time frame and number of times? - ios

After many researches on Stackoverflow and other websites, the only options I can find are either repeat notification on specific day of the week and specific time using:
dateComponents = DateComponents()
dateComponents.calendar = Calendar.current
dateComponents.hour = hour
dateComponents.minute = minute
dateComponents.weekday = 1 // Sunday
let trigger = UNCalendarNotificationTrigger(dateMatching: dateComponents, repeats: true)
// the above will trigger every Sunday at whatever time I set it to be.
Another option to send notification is to set time interval but not much options.
let trigger = UNTimeIntervalNotificationTrigger(timeInterval: 30, repeats: true)
I cannot find any suggestion or solution to trigger notification (number of times starting at certain time on particular day). For example: I want to trigger 5 local notification every Sunday between 8am to 5pm.
Any suggestion? Thank you in advance.

If you want to schedule multiple notifications, you need to create multiple notification triggers and register them.
something like:
// Simple extension if you have to create multiple of the same object
extension DateComponents {
static func triggerFor(hour: Int, minute: Int) -> DateComponents {
var component = DateComponents()
component.calendar = Calendar.current
component.hour = hour
component.minute = minute
component.weekday = 1
return component
}
}
// Add each trigger into an array with the correct date component
let triggers = [
UNCalendarNotificationTrigger(dateMatching: DateComponents.triggerFor(hour: 1, minute: 0), repeats: true),
UNCalendarNotificationTrigger(dateMatching: DateComponents.triggerFor(hour: 2, minute: 0), repeats: true),
UNCalendarNotificationTrigger(dateMatching: DateComponents.triggerFor(hour: 3, minute: 0), repeats: true),
UNCalendarNotificationTrigger(dateMatching: DateComponents.triggerFor(hour: 4, minute: 0), repeats: true),
UNCalendarNotificationTrigger(dateMatching: DateComponents.triggerFor(hour: 5, minute: 0), repeats: true)
]
// Iterate through the array and register the notification with the system
for trigger in triggers {
// Create the content of the notification
let content = UNMutableNotificationContent()
content.title = "My Notification Title"
content.body = "Body of Notification"
// Create the request
let uuidString = UUID().uuidString
let request = UNNotificationRequest(identifier: uuidString,
content: content, trigger: trigger)
// Schedule the request with the system.
let notificationCenter = UNUserNotificationCenter.current()
notificationCenter.add(request) { (error) in
if error != nil {
// Handle any errors.
}
}
}

You would need to create your own times or dates; programming isn't smart and just going to do as you please. Coding is an exact science and needs to be treated as such.
var days = [2, 3, 4, 5, 6]
var hours = [10, 11, 12, 13, 14]
var minute = 0
for day in days {
for hour in hours {
dateComponents = DateComponents()
dateComponents.calendar = Calendar.current
dateComponents.hour = hour
dateComponents.minute = minute
dateComponents.weekday = 2
let trigger = UNCalendarNotificationTrigger(dateMatching: dateComponents, repeats: true)
}
}
This should create a series of triggers that will deliver at the top of the hour for hours of 10 11 12 13 14 on the days of 2 3 4 5 6. In human terms, assuming I have these numbers correlated correctly, it should trigger to your users every hour from 10-2 at the top of the hour on M-F.
Tbh - never used UNCalenderNotificationTrigger, but assuming it works as you state, this will create triggers for each of those times.

Related

Questions about schedule notifications pending approval in Swift

I've created a feature that sounds an alarm at a specific time and day of the week that you set.
func scheduleNotification() {
​
let center = UNUserNotificationCenter.current()
​ let hour = 6
​ let minute = 40
​ let weekdays = [2,3,4] // mon, tue, wed
​ let content = UNMutableNotificationContent()
​
content.title = "Fire!!"
content.body = "test.!"
content.badge = 1
content.categoryIdentifier = "alarm"
content.userInfo = ["customData": "fizzbuzz"]
content.sound = UNNotificationSound.default
​
for weekday in weekdays {
var dateComponents = DateComponents()
dateComponents.hour = hour
dateComponents.minute = minute
dateComponents.weekday = weekday
let trigger = UNCalendarNotificationTrigger.init(dateMatching: dateComponents, repeats: true)
let request = UNNotificationRequest(identifier: UUID().uuidString, content: content, trigger: trigger)
center.add(request)
center.getPendingNotificationRequests { (requests) in
for request in requests {
print("trigger : \(request.trigger)")
}
}
UNUserNotificationCenter.current().add(request) { (error) in
}
}
}
As you can see in the code above, the days you want to repeat are Monday, Tuesday, and Wednesday.
weekdays = [2,3,4]
After that, I wanted to create an alarm that sounds on Monday, Tuesday, and Wednesday using the for statement on the [2,3,4] array.
But the alarm didn't sound as I wanted.
I used the getPendingNotificationRequests method to print a list of scheduled alarms to the console.
trigger : Optional(<UNCalendarNotificationTrigger: 0x283242f20; dateComponents: <NSDateComponents: 0x2830567a0> {
Hour: 6
Minute: 40
Weekday: 2, repeats: YES>)
trigger : Optional(<UNCalendarNotificationTrigger: 0x283241920; dateComponents: <NSDateComponents: 0x283056900> {
Hour: 6
Minute: 40
Weekday: 2, repeats: YES>)
trigger : Optional(<UNCalendarNotificationTrigger: 0x2832416a0; dateComponents: <NSDateComponents: 0x2830556f0> {
Hour: 6
Minute: 40
Weekday: 3, repeats: YES>)
trigger : Optional(<UNCalendarNotificationTrigger: 0x283242f20; dateComponents: <NSDateComponents: 0x283056900> {
Hour: 6
Minute: 40
Weekday: 2, repeats: YES>)
trigger : Optional(<UNCalendarNotificationTrigger: 0x2832426e0; dateComponents: <NSDateComponents: 0x283054c40> {
Hour: 6
Minute: 40
Weekday: 3, repeats: YES>)
trigger : Optional(<UNCalendarNotificationTrigger: 0x2832413c0; dateComponents: <NSDateComponents: 0x283056930> {
Hour: 6
Minute: 40
Weekday: 4, repeats: YES>)
What I wanted was three triggers, and Weekday was stored one by one in the [2,3,4] array.
However, six triggers were created, and the order of Weekday was stored incorrectly.
I want to ask two questions.
If you look at the log of the above code, if you have a series of triggers with Weekday stored as 2, are they executed one by one? For example, does it sound once today if it's Monday and once next Monday?
Why is the alarm stored so wrong, and how can I solve that problem?
The reason you are getting six triggers is because, in your for loop (for weekday in weekdays), you are adding each trigger twice. Each trigger gets added once at the line center.add(request), and again at the line UNUserNotificationCenter.current().add(request) { (error) in. You could resolve this by updating your for loop to look something like this:
for weekday in weekdays {
var dateComponents = DateComponents()
dateComponents.hour = hour
dateComponents.minute = minute
dateComponents.weekday = weekday
let trigger = UNCalendarNotificationTrigger.init(dateMatching: dateComponents, repeats: true)
let request = UNNotificationRequest(identifier: UUID().uuidString, content: content, trigger: trigger)
center.current().add(request) { (error) in
if error != nil {
print(error.localizedDescription)
}
}
}
You also probably only want to print out the triggers after the for loop has completed, not within the for loop. So I would add the following code after the for loop:
func scheduleNotification() {
...
for weekday in weekdays {
...
}
//This following code was originally within your for loop, but should probably execute after the for loop instead
//The console should look a lot cleaner after doing so
center.getPendingNotificationRequests { (requests) in
for request in requests {
print("trigger : \(request.trigger)")
}
}
}

Schedule future UNUserNotificationCenter with condition and limits (iOS)

I know this has been asked several times but there is no concrete solution I have found over the SO.I am working on iOS app and I am using userNotification and all things are working fine but then I have to use the recurring/repeat notifications so here are the conditions that I have to fulfill and wondering how
Case: Show notification to user to check for specific task if it is completed.
show notification everyday until specified date is arrived
Show notification every monday until specified date is arrived
Show notification monthly every specified day until specifed date is arrived
Problem:
I do not know how to schedule repeat notification for given conditions
Is there any limit of notification per app? suppose I have 100 of task and each task may have any of the notification bounded with the uppar given conditions.
Please tell me what could be done? and how I can fulfill my requirements.
show notification everyday until specified date is arrived
This is to display a message at a specific time every day
var dateComponents = DateComponents()
dateComponents.hour = 10
dateComponents.minute = 30
let trigger = UNCalendarNotificationTrigger(dateMatching: dateComponents, repeats: true)
if trigger.nextTriggerDate() >= your_specified_date {
return
}
Is there any limit of notification per app? suppose I have 100 of task
and each task may have any of the notification bounded with the uppar
given conditions.
Yes, there are limits. The system keeps the soonest-firing 64 notifications (with automatically rescheduled notifications counting as a single notification) and discards the rest.
Notification schedule for custom date with body
func schedulingNotificationForCustomDate(body: String, currentDate: Date) {
// Configure User Notification Center
let center = UNUserNotificationCenter.current()
let content = UNMutableNotificationContent()
content.title = ""
content.subtitle = ""
content.body = body
content.categoryIdentifier = "WeekDay notification"
content.userInfo = ["NotificationID": "WeekDay notification", "repeat": true, "reschedule": false]
content.sound = UNNotificationSound.default
let trigger = UNTimeIntervalNotificationTrigger.init(timeInterval: currentDate.timeIntervalSince1970, repeats: true)
let request = UNNotificationRequest(identifier: "WeekDay notification", content: content, trigger: trigger)
center.add(request, withCompletionHandler: nil)
}
Create date as per requirement
extension Date {
static func createDate(weekday: Int, hour: Int, minute: Int, year: Int) -> Date {
let calendar = Calendar.current
var components = DateComponents()
components.hour = hour
components.minute = minute
components.year = year
components.month = calendar.component(.month, from: Date())
components.day = calendar.component(.day, from: Date())
components.weekday = weekday // sunday = 1 ... saturday = 7
components.timeZone = .current
return calendar.date(from: components)!
}
}
Get the next date for scheduling repeat notification
extension Date {
static func nextNthDateFromCurrentDate(FromDate: Date, numberOfDays: Int) -> Date {
return Calendar.current.date(byAdding: .day, value: numberOfDays, to: FromDate)! // where numberOfDays is the number of days for the next date
}
}
For scheduling notification call
self.schedulingNotificationForCustomDate(body: body, currentDate: Date.nextNthDateFromCurrentDate(FromDate: Date.createDate(weekday: 2, hour: 11, minute: 00, year: year), numberOfDays: 14))
There is no end date specification on repeating local notifications, so unfortunately you will need to build something custom. However this is a limit of 64 notifications you may queue up according to Apple.
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.
I would have an extension that can grab the dates between two dates like so:
extension Date {
static func dates(from fromDate: Date, to toDate: Date) -> [Date] {
var dates: [Date] = []
var date = fromDate
while date <= toDate {
dates.append(date)
guard let newDate = Calendar.current.date(byAdding: .day, value: 1, to: date) else { break }
date = newDate
}
return dates
}
}
Then you can grab the dates that you need and iterate through each one to create a notification:
let threeDaysFromNowExample = Calendar.current.date(byAdding: .day, value: 3, to: Date())!
let dates = Date.dates(from: Date(), to: threeDaysFromNowExample)
for date in dates {
let notification = UILocalNotification()
notification.fireDate = date
notification.alertBody = "Your alert here!"
notification.soundName = UILocalNotificationDefaultSoundName
UIApplication.shared.scheduleLocalNotification(notification)
}
Did not test this code, but should get you started. You may need to massage the extension some to add the ability to grab only Mondays for example.

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)

Scheduling notification with Swift 3 for an array of dates

I have an array containing my dates. I want to schedule notifications for those days at 6.30am.
I followed the appcoda tutorial which helps scheduling the notification upon input from a datepicker which is great, but I am a bit uncertain on how to call my function to schedule the notification for only the given days.
So my question is how and where to call the function?
the days are consecutive days
can I give the function a start date and repeat it with the number of items in the array?
Below is my function:
func scheduleNotification(at date: Date) {
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: 6, minute: 30)
let trigger = UNCalendarNotificationTrigger(dateMatching: newComponents, repeats: false)
let content = UNMutableNotificationContent()
content.title = "Advent Calendar"
content.body = "Just a reminder to open your present!"
content.sound = UNNotificationSound.default()
let request = UNNotificationRequest(identifier: "textNotification", content: content, trigger: trigger)
UNUserNotificationCenter.current().removeAllPendingNotificationRequests()
UNUserNotificationCenter.current().add(request) {(error) in
if let error = error {
print("Uh oh! We had an error: \(error)")
}
}
}
Right, so after consulting with a developer #gklka I have decided to use a simple for loop that repeats 24 times ) and it passes the index to the function's day property, where I preconfigured the hour, minute, year and month like so:
func scheduleNotification(day: Int) {
var date = DateComponents()
date.year = 2016
date.month = 11
date.day = day
date.hour = 6
date.minute = 30
let trigger = UNCalendarNotificationTrigger(dateMatching: date, repeats: false)
}
and the for loop:
for index in 1...24 {
scheduleNotification(day: index)
}
Since I had everything set up int AppDelegate I call the function in didFinishLaunchingWithOptions
Update on the 1st of Dec.
So I left everything as is but no notification has occurred in the morning. 😔. I looked into my code to figure out why. There were 2 issues.
I had a line of code within my function that would delete any previous notification set up while iterating through my loop, so I had the below line of code commented out, but things still did not work as expected. UNUserNotificationCenter.current().removeAllPendingNotificationRequests()
I found that I have scheduled my notification with the same requestIdentifier, which basically left me with only 1 notification for the last day. I simply added the index at the end of my custom ID variable with string interpolation, like so: let requestId = "textNotification\(day)"

Scheduling weekly repeatable local notification with fire date from date picker in Swift 3

So I'm currently building a schedule app and I'm trying to create a local notification to fire on specific time on specific day of the week, every week. So the first thing I do is get the date value of the start time of the event, then I subtract 5 minutes from the start time value and then schedule the notification. Previously it was very easy just had to type:
notification.repeatInterval = CalendarUnit.WeekOfYear but now the command is deprecated in Swift 3 and yeah, so the only way I found is:
let someMinutesEarlier = Calendar.current.date(byAdding: .minute, value: -5, to: startTimePicker.date)
let contentOfNotification = UNMutableNotificationContent()
let interval = someMinutesEarlier?.timeIntervalSinceNow
contentOfNotification.title = "Event starting"
contentOfNotification.body = "Some notes"
contentOfNotification.sound = UNNotificationSound.default()
let trigger = UNTimeIntervalNotificationTrigger.init(timeInterval: interval!, repeats: true)
let request = UNNotificationRequest.init(identifier: notificationIdentifier, content: contentOfNotification, trigger: trigger)
let center = UNUserNotificationCenter.current()
center.add(request) { (error) in
print(error as Any)
}
but this only schedules the notification only once (no matter that the repeat boolean is set to true), because of the year in the someMinutesEarlier...or it's may be something else? Any idea?
As McNight mentionned, you can use UNCalendarNotificationTrigger like so:
let interval = 60 * 60 * 24 * 7 - 300 // One week minus 5 minutes.
let alarmTime = Calendar.current.date(byAdding: .second, value: interval, to: Date())!
let components = Calendar.current.dateComponents([.weekday, .hour, .minute], from: alarmTime)
let trigger = UNCalendarNotificationTrigger(dateMatching: components, repeats: true)
(I haven't tested this code, but this should get you on the right path).
More information here.
Edit: Fixed time interval calculation suggested by SwiftyCruz.
Edit 2: Updated to use a Calendar to perform the time shift as suggested by RickiG.
// Swift2.3
func setLNotification(weekDay:Int , hour:Int, min:Int, second:Int, alertBody:String, type:String, isRepeate:Bool){
let calender = NSCalendar(identifier: NSCalendarIdentifierGregorian)
let dateComp: NSDateComponents?
dateComp = calender?.components([.Year,.WeekOfMonth,.Month], fromDate: NSDate())
dateComp?.hour = hour
dateComp?.minute = min
dateComp?.second = 00
dateComp?.weekday = weekDay
dateComp!.timeZone = NSTimeZone.localTimeZone()
print(calender?.dateFromComponents(dateComp!))
let SetCustomDate = calender?.dateFromComponents(dateComp!)
print(SetCustomDate)
let notification = UILocalNotification()
if isRepeate == true{
switch type {
case "Weekly":
notification.fireDate = SetCustomDate!.dateByAddingTimeInterval(60*60*24*7)
notification.repeatInterval = NSCalendarUnit.Weekday
case "2 Weekly":
notification.fireDate = SetCustomDate!.dateByAddingTimeInterval(60*60*24*14)
notification.repeatInterval = NSCalendarUnit.Day
case "Monthly":
notification.fireDate = SetCustomDate!.dateByAddingTimeInterval(60*60*24*28)
notification.repeatInterval = NSCalendarUnit.Day
default:
break;
}
notification.soundName = UILocalNotificationDefaultSoundName
notification.repeatCalendar = calender
}
notification.alertTitle = "STATS"
notification.alertBody = "Please update your Stats detail"
notification.userInfo = ["uid":"reminder"]
print(notification)
UIApplication.sharedApplication().scheduleLocalNotification(notification)
}

Resources