written by https://www.hackingwithswift.com/read/21/2/scheduling-notifications-uilocalnotification
Scheduling notifications: UILocalNotification
Open Main.storyboard in Interface Builder and place two buttons, one above the other. The first should have the title "Register Local" and the second the title "Schedule Local". Add whatever constraints you think sensible, but ideally make them centered horizontally so they fit any device. Using the assistant editor, create an action for each: registerLocal()
and scheduleLocal()
. Now go back to the standard editor and switch to ViewController.swift.
Let me explain how this project needs to work. First, you can't post messages to the user's lock screen unless you have their permission. This was changed in iOS 8, but it's quite sensible – it would, after all, be awfully annoying if any app could bother you when it pleased.
So, in order to send local notifications in our app, we first need to request permission, and that's what we'll put in theregisterLocal()
method. You register your settings based on what you actually need, and that's done with a class calledUIUserNotificationSettings
. For this example we're going to request an alert (a message to show), along with a badge (for our icon) and a sound (because users just love those.)
Once you've created your notification settings object, it's just a matter of calling the registerUserNotificationSettings()
method to tell iOS what you want, and it will then prompt the user if needed. If you requested access before and were denied, nothing will be shown.
Change your registerLocal()
method to be this:
@IBAction func registerLocal(sender: AnyObject) {
let notificationSettings = UIUserNotificationSettings(forTypes: [.Alert, .Badge, .Sound], categories: nil)
UIApplication.sharedApplication().registerUserNotificationSettings(notificationSettings)
}
Helpful tip: if you want to test allowing or denying permission, just reset the simulator and run the app again to get a clean slate. Choose the iOS Simulator menu then "Reset Content and Settings" to make this happen.

Once we have user permission, it's time to fill in the scheduleLocal()
method. This will use the UILocalNotification
class to configure all the data needed to schedule a notification, then call scheduleLocalNotification()
to schedule it for delivery to the user.
We're going to use the following properties:
fireDate
decides when the notification should be shown. iOS tracks this for us, so our app doesn't need to be running when it's time for the notification to be delivered.alertBody
is a string containing the text to show to users. The title of the message will automatically be your app's name.alertAction
is a string shown under your message that completes the sentence, "Slide to…" For example, if you set it be "pericombobulate", it would read "Slide to pericombobulate."soundName
we'll be using the default alert sound, but it's not hard to specify your own – just make sure you include it in the project!userInfo
is a dictionary of keys and values that you can provide. The system does nothing with these other than hand them back to you when the app launches so you can respond.
Here's the first draft of the scheduleLocal()
method:
@IBAction func scheduleLocal(sender: AnyObject) {
let notification = UILocalNotification()
notification.fireDate = NSDate(timeIntervalSinceNow: 5)
notification.alertBody = "Hey you! Yeah you! Swipe to unlock!"
notification.alertAction = "be awesome!"
notification.soundName = UILocalNotificationDefaultSoundName
notification.userInfo = ["CustomField1": "w00t"]
UIApplication.sharedApplication().scheduleLocalNotification(notification)
}
I'm providing a single key/value pair to the userInfo
property; we'll come onto that soon.
That code will trigger a notification five seconds after you click "Schedule Local", so:
- Click "Register Local" and agree to let the app show notifications.
- Click "Schedule Local".
- Before the five seconds are up, press Cmd+L to lock the iOS Simulator screen.
- Wait.
You should see the message appear after a few seconds. Note that the alertAction
text can be quite faint.

Before I move on, our scheduleLocal()
method has a bug: what if the user doesn't grant us permission, and we try showing a local notification? Well, nothing will happen. That's good because your app didn't crash, but it's bad because users will think your app is broken.
To fix the bug, we need to modify scheduleLocal()
so that it checks if we have permission to show local notifications before proceeding. This is as easy as querying the return value of currentUserNotificationSettings()
for our application, and if it's.None
then we need to alert the user and exit the method.
Put this code at the top of the scheduleLocal()
notification:
guard let settings = UIApplication.sharedApplication().currentUserNotificationSettings() else { return }
if settings.types == .None {
let ac = UIAlertController(title: "Can't schedule", message: "Either we don't have permission to schedule notifications, or we haven't asked yet.", preferredStyle: .Alert)
ac.addAction(UIAlertAction(title: "OK", style: .Default, handler: nil))
presentViewController(ac, animated: true, completion: nil)
return
}


| I have set up local notifications in the App Delegate Using this: - (void)applicationDidEnterBackground:(UIApplication *)application
{
UILocalNotification *notification = [[UILocalNotification alloc]init];
[notification setAlertBody:@"Watch the Latest Episode of CCA-TV"];
[notification setFireDate:[NSDate dateWithTimeIntervalSinceNow:5]];
[notification setTimeZone:[NSTimeZone defaultTimeZone]];
[application setScheduledLocalNotifications:[NSArray arrayWithObject:notification]];
}
When I run the app and then quit it I receive an error saying: 2014-06-07 11:14:16.663 CCA-TV[735:149070] Attempting to schedule a local notification{fire date = Saturday, June 7, 2014 at 11:14:21 Pacific Daylight Time, time zone = America/Los_Angeles (PDT) offset -25200 (Daylight), repeat interval = 0, repeat count = UILocalNotificationInfiniteRepeatCount, next fire date = Saturday, June 7, 2014 at 11:14:21 Pacific Daylight Time, user info = (null)} with an alert but haven't received permission from the user to display alerts
How can I get the necessary permission to display the alerts? |
| |
| Since iOS 8 you need to ask user's permission to show notifications from your app, this applies for both remote/push and local notifications. In Swift you can do it like this, Update for Swift 2.0 func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: NSDictionary?) -> Bool {
// Override point for customization after application launch.
if(UIApplication.instancesRespondToSelector(Selector("registerUserNotificationSettings:")))
{
let notificationCategory:UIMutableUserNotificationCategory = UIMutableUserNotificationCategory()
notificationCategory.identifier = "INVITE_CATEGORY"
notificationCategory .setActions([replyAction], forContext: UIUserNotificationActionContext.Default)
//registerting for the notification.
application.registerUserNotificationSettings(UIUserNotificationSettings(forTypes:[ .Sound, .Alert,
.Badge], categories: nil)
}
else
{
//do iOS 7 stuff, which is pretty much nothing for local notifications.
}
return true
}
Objective C syntax is also very similar. - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
if ([UIApplication instancesRespondToSelector:@selector(registerUserNotificationSettings:)]){
[application registerUserNotificationSettings:[UIUserNotificationSettings settingsForTypes:UIUserNotificationTypeAlert|UIUserNotificationTypeBadge|UIUserNotificationTypeSound categories:nil]];
}
// Override point for customization after application launch.
return YES;
}
To check for currently registered notification types you can use UIApplications method, - (UIUserNotificationSettings *)currentUserNotificationSettings
So if the user has said no to your app then this function should return a setting without any types in it. I have written a tutorial about this, you could see it here. | | answered Jun 11 '14 at 11:35 |
|
| |


| Put this code in the view controller where you will first program the notifications (if you program them at launch, then it will be application:didFinishLaunchingWithOptions: ): if ([UIApplication instancesRespondToSelector:@selector(registerUserNotificationSettings:)]) {
[[UIApplication sharedApplication] registerUserNotificationSettings:[UIUserNotificationSettings settingsForTypes:UIUserNotificationTypeAlert|UIUserNotificationTypeSound categories:nil]];
}
In Swift: if(UIApplication.instancesRespondToSelector(Selector("registerUserNotificationSettings:"))) {
UIApplication.sharedApplication().registerUserNotificationSettings(UIUserNotificationSettings(forTypes: .Alert | .Sound, categories: nil))
}
The solutions that test against system version number are sub-optimal and error-prone. | | answered Sep 9 '14 at 0:47 |
|
| |
| Try this for Objective-C: - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions: (NSDictionary *)launchOptions
{
// are you running on iOS8?
if ([application respondsToSelector:@selector(registerUserNotificationSettings:)])
{
UIUserNotificationSettings *settings = [UIUserNotificationSettings settingsForTypes:(UIUserNotificationTypeBadge|UIUserNotificationTypeAlert|UIUserNotificationTypeSound) categories:nil];
[application registerUserNotificationSettings:settings];
}
else // iOS 7 or earlier
{
UIRemoteNotificationType myTypes = UIRemoteNotificationTypeBadge | UIRemoteNotificationTypeAlert | UIRemoteNotificationTypeSound;
[application registerForRemoteNotificationTypes:myTypes];
}
}
For Swift: func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: NSDictionary?) -> Bool {
// Override point for customization after application launch.
if(UIApplication.instancesRespondToSelector(Selector("registerUserNotificationSettings:")))
{
application.registerUserNotificationSettings(UIUserNotificationSettings(forTypes: UIUserNotificationType.Sound | UIUserNotificationType.Alert | UIUserNotificationType.Badge, categories: nil))
}
else
{
//
}
return true
}
| | answered Nov 12 '14 at 8:42 |
|
| |
| I just faced the same problem. Seems like in iOS 8 we need to do an additional step, usually done inside: - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { /*...*/ }
You can use this code if you want to keep it backward compatible: #if __IPHONE_OS_VERSION_MAX_ALLOWED >= 80000
if ([UIApplication instancesRespondToSelector:@selector(registerUserNotificationSettings:)])
{
[[UIApplication sharedApplication] registerUserNotificationSettings:[UIUserNotificationSettings settingsForTypes:UIUserNotificationTypeAlert | UIUserNotificationTypeBadge | UIUserNotificationTypeSound categories:nil]];
}
#endif
The system will remember the decision, and will only ask once. |
applicationWillEnterBackground
– Midhun MP Jun 7 '14 at 18:28registerUserNotificationSettings
. Had it been iOS 8, this thread would have answered your question. But, g ahead have a look -stackoverflow.com/questions/24006998/… – raurora Jun 7 '14 at 20:43