Local Notifications in iOS 10

James Rochabrun
9 min readNov 9, 2016

--

Local Notifications are a great way to send notifications to the user without the necessity of an internet connection or server side programming. These notifications are perfect for any kind of “reminder type apps”, and are available since the release of iOS 4. If you are programming for a while I am sure you are familiar with the UILocalNotification class, and right now with the arriving of iOS 10, I bet you are as happy as me (this was said in a very sarcastic way), that UILocalNotification is been deprecated.

Well, new updates come with new code implementations and as developers, we should keep our code updated, right?

Ok, let’s cut the chit-chat and let’s build a very simple app that will remind us to go for a run, using the new UserNotifications Framework's UNNotificationRequest. Start by creating a new project in Xcode and add the UserNotifications Framework in your Linked Frameworks and Libraries section.

Go to your AppDelegate.m file and import the framework like this...

Now, inside the didFinishLaunchingWithOptions: copy and paste this...

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
// Override point for customization after application launch.
if ([application respondsToSelector:@selector(isRegisteredForRemoteNotifications)]) {

UNUserNotificationCenter *center = [UNUserNotificationCenter currentNotificationCenter];
[center requestAuthorizationWithOptions:(UNAuthorizationOptionAlert + UNAuthorizationOptionSound + UNAuthorizationOptionBadge) completionHandler:^(BOOL granted, NSError * _Nullable error) {
if (!granted) {
//Show alert asking to go to settings and allow permission
}
}];
}
application.applicationIconBadgeNumber = 0;
return YES;
}

The if statement basically checks if the app responds to a specific selector of a framework, if not, probably the app it’s not updated for iOS 10, in this case. The UNUserNotificatinCenter manages the notification-related activities for your app, we use this object to request Authorization to interact with the user when local (and remote) notifications arrive, now run the app, and you will see the prompt asking for authorization, don’t forget to allow authorization, if not, you won’t see any notification later.

Now, go to your viewController file and let’s start with the UI, import the <UserNotifications/UserNotifications.h> and add a UIButton as a property like this…

Inside ViewDidLoad let’s change the backgroundColor of the main View and add the UIButton, copy and paste…

- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
self.view.backgroundColor = [UIColor colorWithRed:9.0/255.0 green:26.0/255.0 blue:56.0/255.0 alpha:1.0];
_button = [UIButton new];
_button.backgroundColor = [UIColor colorWithRed:221.0/255.0 green:27.0/255.0 blue:73.0/255.0 alpha:1.0];
[_button setTitle:@"SET TIMER" forState:UIControlStateNormal];
[_button addTarget:self action:@selector(generateLocalNotification:) forControlEvents:UIControlEventTouchUpInside];
_button.layer.cornerRadius = 20;
[self.view addSubview:_button];
}

Now, let’s lay out the UIButton programmatically in the main View, you can do all of this in interface builder of course, but I like to do it in code, if you are new with iOS and want to learn more about laying out views in code, check this link. Add to your file the viewWillLayoutSubviews method, copy and paste…

- (void)viewWillLayoutSubviews {
[super viewWillLayoutSubviews];

CGRect frame = _button.frame;
frame.size.height = 50;
frame.size.width = 200;
frame.origin.x = (self.view.frame.size.width - frame.size.width) /2;
frame.origin.y = (self.view.frame.size.height - frame.size.height) /2;
_button.frame = frame;
}

Right now you are receiving a warning that says “Undeclared Selector”, this means that we didn’t declare this method yet, so let’s do it, and inside it let’s add the implementation to add a Local notification when the user taps the button, copy and paste…

- (void)generateLocalNotification:(id)sender {

UNMutableNotificationContent *localNotification = [UNMutableNotificationContent new];
localNotification.title = [NSString localizedUserNotificationStringForKey:@"Time for a run!" arguments:nil];
localNotification.body = [NSString localizedUserNotificationStringForKey:@"BTW, running late to happy hour does not count as workout" arguments:nil];
localNotification.sound = [UNNotificationSound defaultSound];
UNTimeIntervalNotificationTrigger *trigger = [UNTimeIntervalNotificationTrigger triggerWithTimeInterval:10 repeats:NO];

localNotification.badge = @([[UIApplication sharedApplication] applicationIconBadgeNumber] +1);

UNNotificationRequest *request = [UNNotificationRequest requestWithIdentifier:@"Time for a run!" content:localNotification trigger:trigger];

UNUserNotificationCenter *center = [UNUserNotificationCenter currentNotificationCenter];
[center addNotificationRequest:request withCompletionHandler:^(NSError * _Nullable error) {
NSLog(@"Notification created");
}];
}

What is all this? to generate a notification we need to create first, a UNMutableNotificationContentwhich is the object that provides the editable content for a notification, set it’s properties title, body, sound (which can be customized), and for the badge property let’s pass the value of the applicationIconBadgeNumber property of the UIApplication, plus 1. Then create an instance of UNTimeIntervalNotificationTrigger, this object triggers the delivery of a local notification after a specified amount of time, in this case, we set it to 10 seconds. The next step is to create a request using UNNotificationRequest and add it to an instance of UNUserNotificationCenter with the method addNotificationRequest:withCompletionHandler, now run the app, press the button and immediately press CMD + SHIFT + H to go to simulator home, wait…and, Tururu! you will see the red badge icon of the app with the number 1 and the banner popping up!

If you open the app and then you press CMD + SHIFT + H to send it to the foreground again, you will notice that the badge it’s still showing that you have a notification, an easy fix is to add this line of code in applicationWillEnterForeground: in your AppDelegate.m, like this…

ok, that’s cool but it will be better if we can actually do something when the user taps the notification, and also to show the notification even when the user is using the app, and for that, we will implement the UNUserNotificationCenterDelegate protocol. BTW, If you are not familiar with delegation I suggest checking apple documentation related to that topic, Start by adding the protocol like this…

Then add this line inside the generateLocalNotification method above the UNUserNotificationCenter instance…

center.delegate = self;

Now, let’s implement the delegate methods! but first let’s create a method to display an alert that will show the content of the Notification to the user, copy and paste …

- (void)takeActionWithLocalNotification:(UNNotification *)localNotification {

UIAlertController *alertController = [UIAlertController alertControllerWithTitle:localNotification.request.content.title message:localNotification.request.content.body preferredStyle:UIAlertControllerStyleAlert];
UIAlertAction *ok = [UIAlertAction actionWithTitle:@"OK" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) {
NSLog(@"ok");
}];
[alertController addAction:ok];
dispatch_async(dispatch_get_main_queue(), ^{
[self presentViewController:alertController animated:YES completion:^{
}];
});
}

This method takes a UNNotification as a parameter and uses it to display the content of the notification, in this case, the title and body; don’t forget to present the alert in the main queue ;)

First I will implement the delegate method willPresentNotification:withCompletionHandler: to show the notification to the user while using the app… that’s right, copy and paste one more time :)

- (void)userNotificationCenter:(UNUserNotificationCenter *)center willPresentNotification:(UNNotification *)notification withCompletionHandler:(void (^)(UNNotificationPresentationOptions))completionHandler {

UIAlertController *alertController = [UIAlertController alertControllerWithTitle:@"Notification alert" message:@"This app just sent you a notification, do you want to see it?" preferredStyle:UIAlertControllerStyleAlert];
UIAlertAction *ignore = [UIAlertAction actionWithTitle:@"IGNORE" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) {
NSLog(@"ignore");
}];
UIAlertAction *see = [UIAlertAction actionWithTitle:@"SEE" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) {

[self takeActionWithLocalNotification:notification];
}];

[alertController addAction:ignore];
[alertController addAction:view];

[self presentViewController:alertController animated:YES completion:^{
}];
}

Inside this method we create an alert that will tell us that we just got a notification, and if we want to see it, you can see that we are using the method that we create before and we are passing the notification as a parameter, now run the app press the button and just wait for the prompt and press “SEE”, and there it is, its time for a run!

Finally, I will show you how to use the delegate method didReceiveNotificationResponse:(UNNotificationResponse *)response withCompletionHandler: to perform an action when the user taps in the banner, for this purposes we are going to reuse the method that we create at the beginning takeActionWithLocalNotification: that accepts a UNNotification object as a parameter, copy and paste…

- (void)userNotificationCenter:(UNUserNotificationCenter *)center didReceiveNotificationResponse:(UNNotificationResponse *)response withCompletionHandler:(void (^)())completionHandler {
[self takeActionWithLocalNotification:response.notification];
}

Now, run the app, press the button and CMD + SHIFT + Hto send it to the foreground, wait for the banner and once it shows up tap on it, the app will open showing you the alert with the content of the notification.

And that’s it, you can implement any action inside this methods, like in this case, show a reminder.
But wait, stop it, I can feel your look, and you are right; it’s always better to let the user decide how often will exercise, correct? so let’s add a UIDatePicker to accomplish that.

Start adding these properties…

@property (nonatomic, assign) NSTimeInterval countDownInterval;
@property (nonatomic, strong) UIDatePicker *timePicker;

Replace the implementation of viewDidLoad for this one….

- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
self.view.backgroundColor = [UIColor colorWithRed:9.0/255.0 green:26.0/255.0 blue:56.0/255.0 alpha:1.0];

_timePicker = [UIDatePicker new];
_timePicker.backgroundColor = [UIColor clearColor];
_timePicker.tintAdjustmentMode = UIViewTintAdjustmentModeAutomatic;
[_timePicker addTarget:self action:@selector(userSelectTime:) forControlEvents:UIControlEventValueChanged];
[_timePicker setValue:[UIColor colorWithRed:253.0/255.0 green:210.0/255.0 blue:5.0/255.0 alpha:1.0] forKey:@"textColor"];
_timePicker.datePickerMode = UIDatePickerModeCountDownTimer;
dispatch_async(dispatch_get_main_queue(), ^{
_timePicker.countDownDuration = _countDownInterval;
});
[self.view addSubview:_timePicker];

_button = [UIButton new];
_button.backgroundColor = [UIColor colorWithRed:221.0/255.0 green:27.0/255.0 blue:73.0/255.0 alpha:1.0];
[_button setTitle:@"SET TIMER" forState:UIControlStateNormal];
[_button addTarget:self action:@selector(generateLocalNotification:) forControlEvents:UIControlEventTouchUpInside];
_button.layer.cornerRadius = 20;
_button.alpha = 0.3;
_button.userInteractionEnabled = NO;
[self.view addSubview:_button];
}

I won’t be very detailed about this because the core of this post is how to use Local Notifications, but in a glance, we just added a UIDatePicker with a method that will update our “countDownInterval” property when a value in the picker change. Also, we changed the alpha of the button to 0.3, and set the user interaction to disable. Now let’s change the implementation in the viewWillLayoutSubviews method, replace the implementation for this…

- (void)viewWillLayoutSubviews {
[super viewWillLayoutSubviews];

CGRect frame = _timePicker.frame;
frame.size.height = _timePicker.intrinsicContentSize.height;
frame.size.width = self.view.frame.size.width;
frame.origin.x = 0;
frame.origin.y = (self.view.frame.size.height - frame.size.height) /2;
_timePicker.frame = frame;

frame = _button.frame;
frame.size.height = 50;
frame.size.width = 200;
frame.origin.x = (self.view.frame.size.width - frame.size.width) /2;
frame.origin.y = ((CGRectGetMaxY(self.view.frame) - CGRectGetMaxY(_timePicker.frame)) - frame.size.height) / 2 + CGRectGetMaxY(_timePicker.frame);
_button.frame = frame;

}

Now, inside the method generateLocalNotification: find the method triggerWithTimeInterval:and pass our global property "countDownInterval" as a parameter like this...

UNTimeIntervalNotificationTrigger *trigger = [UNTimeIntervalNotificationTrigger triggerWithTimeInterval:_countDownInterval repeats:NO];

Finally, let’s declare the method userSelectTime:, this method will update the "countDownInterval" property and will enable the UIButton if the "countDownInterval" is greater than 2 minutes (in this case), remember that you can change the value inside the if statement for the one that you want, for example, you can set it to 3600 if you want to make the notification with a min period of one hour, copy and paste...

- (void)userSelectTime:(UIDatePicker *)sender {

_countDownInterval = (NSTimeInterval)sender.countDownDuration;
if (_countDownInterval >= 120) {
_button.alpha = 1;
_button.userInteractionEnabled = YES;
} else {
_button.alpha = 0.3;
_button.userInteractionEnabled = NO;
}
}

Now run the app!

You will see that the button turns to enable when you change the picker to a value that its more than two minutes, tap the button and just wait for it!

As you saw, LocalNotifications for iOS 10 are not that hard to implement and are a very useful tool . Last one thing that I want to mention, although its good practice keep your code updated, you as a developer, also should be aware of how to handle maintaining backward compatibility in your applications, remember that not every user updates it’s phones that fast, and that new frameworks can break your app in devices that are not updated, if you want to know more about this check this link.

You can clone the full project here, happy jogging.

Peace!

James Rochabrun — Founder Start App Studio

--

--