I'm trying to use local notification, this is my code:
appdelegate
application.registerUserNotificationSettings(UIUserNotificationSettings(forTypes: [UIUserNotificationType.Sound, UIUserNotificationType.Alert, UIUserNotificationType.Badge], categories: nil))
notificationViewController
let localNotification:UILocalNotification = UILocalNotification()
var BDate = friend.birthday.componentsSeparatedByString("/")
let date = NSDate.date(year: 2015, month: Int(BDate[1])!, day: Int(BDate[0])! - daysBefore, hour: hour, minute: min, second: 0)
localNotification.soundName = "notificationSound.mp3"
localNotification.alertBody = friend.fullName + " has a birthday today!"
localNotification.fireDate = date
localNotification.timeZone = NSTimeZone.localTimeZone()
localNotification.repeatInterval = NSCalendarUnit.Year
UIApplication.sharedApplication().scheduleLocalNotification(localNotification)
friend.birthday is a string - "DD/MM/YYYY"
I'm calling the setNotification function for every friend in a friends array. When there are only one or two friends I get the notification but one there are ~100+ I no longer get the notification.
I know the fireDate is correct, I checked it.
Why the code isn't working?
Each app on a device is limited to 64 scheduled local notifications. The system discards scheduled notifications in excess of this limit, keeping only the 64 notifications that will fire the soonest. Recurring notifications are treated as a single notification.
You can find more detail here
Looks like you're really exceeded the notifications limit. There's several tips and guidelines that might help:
Try to check your date versus NSDate() and schedule notification only if date.timeIntervalSinceReferenceDate > NSDate().timeIntervalSinceReferenceDate (There is a compare method in NSDate, but I prefer to compare raw values)
Scheduling local notifications one-by-one is very expensive for application performance, so you can form an array of notifications and schedule them all at once. Use UIApplication.sharedApplication().scheduledLocalNotifications property for that purpose.
This will allow you effectively reschedule all notifications on every application launch. For example in applicationDidFinishLaunching() delegate method.
And also, this will give you another advantage: you can easily check for notifications limit and, if necessary, add/replace 64-th notification with prompt to user to launch your app to allow it to schedule more notifications. This is a common practice for many applications to deal with Apple's limitations.
Related
I'm working with local notification on iOS, but I'm having some problems when iOS tries to create the notifications. Some of them are created and some won't.
After reviewing my code a lot, I found that it was failing at the point where it was creating the local notification.
UIApplication.sharedApplication().scheduleLocalNotification(notification)
The only reason I could think of was that creating the notifications inside a loop, around 50-60 notifications, was too much for iOS to process. I'm doing it this way because all the notifications have a different time and different day, and belong to different things.
This is my block to create the local notifications:
let createdUid = self.generateNotificationUUID()
// create a corresponding local notification
let notification = UILocalNotification()
/* Time and timezone settings */
notification.fireDate = self.buildTime()
notification.repeatInterval = NSCalendarUnit.WeekOfYear
notification.timeZone = NSTimeZone.systemTimeZone()
/* Information settings */
notification.alertBody = "Sector \(notificationData["sector"]!): located at \(notificationData["name"]!) closes in 15 min."
notification.alertAction = "Open"
/* Badge settings */
notification.applicationIconBadgeNumber = UIApplication.sharedApplication().applicationIconBadgeNumber + 1
notification.soundName = "ring.caf"
notification.userInfo = ["UUID": createdUid, ]
/* Schedule the notification */
UIApplication.sharedApplication().scheduleLocalNotification(notification)
And all this code is inside a loop. The same loop before calling the notification creation, dynamically builds the notificationData array.
The notification data array contains the sector, the name, and the time and day.
Time and day is used to calculate the notification fire date.
Sector and Name are used for the alert body.
And all the four values are used to generate the UID (UUID).
If I put a print and remove the UIApplication.sharedApplication()... all the data looks good and what it needs to be.
I've tried, to solve the problem, using
dispatch_async(dispatch_get_main_queue()) {...}
and
dispatch_sync(dispatch_get_main_queue()) {...}
But with async I received the same result that I had without it, and using sync my screen freezes.
I kind of lost with this, I'm sure that my issues are because iOS doesn't process in time all the notification creation, but I dunno how to fix it.
I hope someone can help me, I'm using XCode 7.3.1 and Swift 2.2
From the documentation: https://developer.apple.com/library/ios/documentation/iPhone/Reference/UILocalNotification_Class/
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'm making an application where I want local notifications. And something strange is happening. I schedule the notification:
static func setNotification(body: String, departure: Double, notification_info: Dictionary<String, String> )
{
let notification = UILocalNotification()
notification.alertBody = body
notification.fireDate = NSDate(timeIntervalSinceNow: departure)
notification.alertAction = "ShowDetails"
notification.userInfo = notification_info
notification.soundName = UILocalNotificationDefaultSoundName
UIApplication.sharedApplication().scheduleLocalNotification(notification)
print("notification over: " + String(departure))
print("-------" + String( notification.fireDate ))
}
I print in how much seconds i am supposed to get an notification.
I go to background mode, and keep watching when I will get an notification. when the time has passed, I get no notification, even though I am sure I am in background mode. (within Xcode I look at the debug navigator > Energy Impact, and it is saying I am in background).
When I restart my phone, and run the application, it does show the notification. Everything works perfect. And then later, after some more testing and using the application, my notifications stop working again (even though the notifications are still scheduled. I am following all the scheduled notifications with:
UIApplication.sharedApplication().scheduledLocalNotifications
I am still new with Swift, and I have no idea why this is happening. It's making me crazy not knowing why my notification is not firing, even though it is scheduled and everything...
Could anyone help me? If you need more information, please ask.
Edit:
I did set the permissions
The notifications are set in the AppDelegate
The departure time definitely is right
I made a mistake by not adding an unique ID to the notification (notification.userInfo was not always unique), and overwrites the previous scheduled notification. Thats why it something does work and sometimes doesn't.
Fixed the problem by making it unique.
Thanks
Since iOS 8.0, You have to ask for permission for local notifications as remote notifications:
let notificationSettings = UIUserNotificationSettings(forTypes: UIUserNotificationType.Alert, categories: nil)
UIApplication.sharedApplication().registerUserNotificationSettings(notificationSettings)
See more details in Location Notifications
Main question: Should I be using local notifications, alarms, or reminders if I want attach due dates to tasks in my app? I want them to get a notification even if the app isn't running when the deadline arrives.
I found this tutorial on using UILocalNotification which it says can:
gives us the ability to cast notifications to a user without running the application
However, it was written six years ago and also states that this was introduced in iOS4. I know that a lot changes over 5 iOS versions.
I also read, alternatively, that I could use Event Kit. However, that seems more complicated than UILocalNotification.
Lastly, I could use probably take the current date/time and reminder date/time and create a timer to countdown.
So if I just want to attach due dates to tasks in my app (they don't need to show up in reminders or in the calendar), what's the best method and why?
I found following chunk of code here. This is also a good tutorial for how to create event in application.
var notification = UILocalNotification()
notification.alertBody = "Todo Item \"\(item.title)\" Is Overdue" // text that will be displayed in the notification
notification.alertAction = "open" // text that is displayed after "slide to..." on the lock screen - defaults to "slide to view"
notification.fireDate = item.deadline // todo item due date (when notification will be fired)
notification.soundName = UILocalNotificationDefaultSoundName // play default sound
notification.userInfo = ["UUID": item.UUID, ] // assign a unique identifier to the notification so that we can retrieve it later
notification.category = "TODO_CATEGORY"
UIApplication.sharedApplication().scheduleLocalNotification(notification)
I have a icon badge number update requirement. The app tracks tasks. I want the app to have a badge displaying the number of tasks due on each day. There are basically two cases when the badge number needs to be updated:
Midnight every day.
If new tasks are added or tasks are removed.
I know how to handle the second case. I can set badge number in the applicationResignActive func. However, the midnight automatic update is trick for me. To update the badge number, I need to call a func of the app to count the tasks that due on the day. However, in midnight, the app may be in all possible situations: foreground, background and not running. How can I do this? Thank you.
=====================================
To be clearer with my requirement, I would like the badge number to be updated everyday correctly, even the user never opens the app for a whole day or for consecutive several days. Also, I would try to avoid server side support because the app is a standalone app so far. Much appreciated for any help.
=====================================
Final update: I accepted Vitaliy's answer. However, his answer requires the app to be opened at least once every day. Otherwise, the event won't fire and the badge number cannot be updated.
Also, in my case, every time the app enters background event fires, I have to remove the existing notification and schedule a new one, with the up-to-dated badge number recalculated.
I am still interested in some way to handle the case that the app is not opened every day, how can you make sure the badge number is correct. So far, the easiest way is to setup some server and let it push notifications to the app regularly.
You can achieve it with UILocalNotification:
When app goes to background, calculate exact badge count number for nearest midnight
Schedule UILocalNotification at the nearest midnight with your calculated badge count
You will get notification at midnight, and app's badge count will be updated
Example code:
- (void)applicationDidEnterBackground:(UIApplication *)application {
// Calculate nearest midnight or any other date, which you need
NSDate *nearestMidnight = [self nearestMidnight];
// Create and setup local notification
UILocalNotification *notification = [UILocalNotification new];
notification.alertTitle = #"Some title";
notification.alertBody = #"Some message";
notification.fireDate = nearestMidnight;
// Optional set repeat interval, if user didn't launch the app after nearest midnight
notification.repeatInterval = NSCalendarUnitDay;
// Calculate badge count and set it to notification
notification.applicationIconBadgeNumber = [self calculateBadgeCountForDate:nearestMidnight];
[application scheduleLocalNotification:notification];
}
I have a set of UILocalNotifications that I am scheduling. They are firing on time and seem to be working fine. However, on occasion, when a NEW notification fires there are multiple (sometimes only one) OLD notifications that fire along with it.
For example, I schedule a UILocalNotification to take out the trash on Monday at 5pm with no repeat interval. It fires no problem and on time. On Tuesday, I have a UILocalNotification to bring in the trash bins for Tuesday at 5pm, again with no repeat interval. When that one fires, I will see the correct notification for NOW, but also below the current notification will be another notification to take the trash out 1 Day Ago. I have not rescheduled this notification. It appears to be the notification from yesterday.
It is very bizarre and I cannot reproduce it on any sort of consistent basis. I thought that maybe there were some old notifications being added somehow so I added some logic to run through all scheduled notifications and remove any that had a fire date that was in the past but that did not help. Anyone else ever see this problem? Is there some manual clearing of a notification that I need to do when one fires?
EDIT: Added some of the scheduling code
//only schedule if the alert date is later than now
if ([AMDateUtil isNowEarlierThanDate:alertDate]) {
//create the notification and setup some properties
UILocalNotification *localNotification = [[UILocalNotification alloc] init];
localNotification.fireDate = alertDate;
localNotification.timeZone = [NSTimeZone defaultTimeZone];
localNotification.alertAction = nil;
localNotification.soundName = UILocalNotificationDefaultSoundName;
//add the local notification
[[UIApplication sharedApplication] scheduleLocalNotification:localNotification];
}
We had the same issue, but managed to fix it by batching our calls to the API. Instead of calling scheduleLocalNotification each time, instead build an array of all notifications you want to be scheduled. Like this:
NSMutableArray *notifications = [[[UIApplication sharedApplication] scheduledLocalNotifications] mutableCopy];
// add notifications to the mutable array
// same goes for removing notifications
[[[UIApplication sharedApplication] setScheduledLocalNotifications:notifications];
Also make sure you are calling it from the main thread.
Our problems seem to have gone away after doing this.
The first thing that I can think of is to check the repeatInterval of your notifications. It seems that you may want your repeatInterval to be weekly, but the interval seems to be set to daily. To set the repeatInterval to weekly use:
localNotification.repeatInterval = NSWeekCalendarUnit;
Perhaps in some places you may be accidentally using
localNotification.repeatInterval = NSWeekdayCalendarUnit;
which repeats once a day.
If that is not the issue, then perhaps if you post some of your code where you schedule the notifications someone could help you. If a notification has a repeat interval of 0 (or not repeating) then you should not have to manually clear it. If the repeat interval is not 0 then you will have to manually clear it to get it to stop repeating.