iPhone Programming Tutorial – Local Notifications

July 29th, 2010 Posted by: brandontreb - posted under:Tutorials

Way back when, when everyone was still complaining about Apple’s lack of support for (3rd party) multitasking, there was a simple solution put in place. This solution was known as push notifications.

Push notifications solved many of the issues associated with background processing.  For example, when quitting the AIM application, the server could keep you logged in and send you a push notification when a new message arrived.  You could then tap on a View button that would launch the app.

This solution is great and all, but it still requires that you have an active internet connection.  As of iOS4, Apple has introduced a new type of notification that can be scheduled to fire within the device itself.  It requires no complicated server programming, or additional configuration with iTunes.  I am talking about Local Notifications.

Local notifications can be scheduled on the user’s iDevice to fire at any given time; you can even set them to be recurring.  Today, we will explore these notifications and I will provide you with a simple example of how to schedule, view, and handle local notifications.  Here is a quick screenshot of the project that we are going to create (note I am using the iPhone 4  simulator).

The project will allow a user to schedule a location notification to fire off at a given date.  Using the text field, they are also able to specify some text for the notification.  The table view below displays a list of all of the currently scheduled location notifications within the application.

So, by now (if you are an avid iCodeBlog reader), you are already a killer iPhone dev and I can rush through the stuff that is not related to the notifications.  I will try to provide links to tutorials about sections that I rush through as well.

1. Create a View-Based Application

We will be using this as our starting point.  Check out this tutorial if you are unfamiliar with doing this.  Name the project Notifier.

2. Create All Properties and Dummy IBActions

This is usually a good first step when tackling an application like this.  Let’s get everything set up in the .h and .m files so we only have to visit Interface Builder Once.  Here is what our NotifierViewController.h file looks like.

@interface NotifierViewController : UIViewController<UITableViewDelegate,UITableViewDataSource> {
        IBOutlet UITableView *tableview;
        IBOutlet UIDatePicker *datePicker;
        IBOutlet UITextField *eventText;
}
 
@property (nonatomic, retain) IBOutlet UITableView *tableview;
@property (nonatomic, retain) IBOutlet UIDatePicker *datePicker;
@property (nonatomic, retain) IBOutlet UITextField *eventText;
 
- (IBAction) scheduleAlarm:(id) sender;
 
@end

Seems clear enough.  We have 3 UI elements that we care about and one action.  One thing to note is, your class should implement the UITableViewDelegate and UITableViewDataSource protocols.  This is because we will be displaying a tableview containing all of the scheduled alarms.

Now, do all of the necessary steps in your .m file.  This includes memory management for the IBOutlets and creating a dummy method for the scheduleAlarm IBAction.  Your .m file should look something like this. Note: I have omitted import statements because my syntax highlighter wasn’t digging them.

@implementation NotifierViewController
 
@synthesize datePicker,tableview, eventText;
 
- (IBAction) scheduleAlarm:(id) sender {
 
}
 
- (void)didReceiveMemoryWarning {
    [super didReceiveMemoryWarning];
}
 
- (void)viewDidUnload {
        datePicker = nil;
        tableview = nil;
        eventText = nil;
}
 
- (void)dealloc {
    [super dealloc];
}
 
@end

Now it’s time to build our interface.  Open Interface builder and construct an interface like this.

If you want my super sweet green button image, here it is:

After creating the interface, make sure you hook up all of the UI components to their corresponding IBOutlets and hook up the touchUpInside: method of the button the your scheduleAlarm: IBAction.  For more info on hooking up IBOutlets, check out this tutorial.

3. Implement UITableViewDelegate and UITableViewDataSource Delegate methods to List Currently Scheduled Local Notifications

It may seem weird to implement the code to display the notifications before the code that creates them, however I like this approach better.  This way, once we schedule the notifications, they automagically appear in our table.  Add the following code to your .m file.

- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
    // We only have one section
    return 1;
}
 
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
    // Return the number of notifications
    return [[[UIApplication sharedApplication] scheduledLocalNotifications] count];
}
 
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
 
    static NSString *CellIdentifier = @"Cell";
 
    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
    if (cell == nil) {
        cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:CellIdentifier] autorelease];
    }
 
    // Get list of local notifications
    NSArray *notificationArray = [[UIApplication sharedApplication] scheduledLocalNotifications];
    UILocalNotification *notif = [notificationArray objectAtIndex:indexPath.row];
 
    // Display notification info
    [cell.textLabel setText:notif.alertBody];
    [cell.detailTextLabel setText:[notif.fireDate description]];
 
    return cell;
}

OK, finally some “real” code.  Most of this code should seem pretty straight forward.  If not, check out this tutorial on UITableViews.

So, the new code here is dealing with retrieving a list of scheduled notifications.  Calling the scheduledLocalNotifications method of UIApplication will return an NSArray of all notifications scheduled by the current app.  We just index into this array and grab each notification.

Finally, we are displaying the alertBody (text that displays when the notification fires) and the fireDate (date and time when the notification will display) in the tableview cell.

4. Scheduling Notifications

And now for the moment you’ve been waiting for… OK, probably not, but definitely the most exciting (least boring) part of this tutorial.  Let’s implement that scheduleAlarm: IBAction that you framed out earlier.  Update your .m file to contain the following code.

- (IBAction) scheduleAlarm:(id) sender {
    [eventText resignFirstResponder];
 
    NSCalendar *calendar = [NSCalendar autoupdatingCurrentCalendar];
 
    // Get the current date
    NSDate *pickerDate = [self.datePicker date];
 
    // Break the date up into components
    NSDateComponents *dateComponents = [calendar components:( NSYearCalendarUnit | NSMonthCalendarUnit |  NSDayCalendarUnit )
                                                                                                   fromDate:pickerDate];
    NSDateComponents *timeComponents = [calendar components:( NSHourCalendarUnit | NSMinuteCalendarUnit | NSSecondCalendarUnit )
                                                                                                   fromDate:pickerDate];
    // Set up the fire time
    NSDateComponents *dateComps = [[NSDateComponents alloc] init];
    [dateComps setDay:[dateComponents day]];
    [dateComps setMonth:[dateComponents month]];
    [dateComps setYear:[dateComponents year]];
    [dateComps setHour:[timeComponents hour]];
        // Notification will fire in one minute
    [dateComps setMinute:[timeComponents minute]];
        [dateComps setSecond:[timeComponents second]];
    NSDate *itemDate = [calendar dateFromComponents:dateComps];
    [dateComps release];
 
    UILocalNotification *localNotif = [[UILocalNotification alloc] init];
    if (localNotif == nil)
        return;
    localNotif.fireDate = itemDate;
    localNotif.timeZone = [NSTimeZone defaultTimeZone];
 
        // Notification details
    localNotif.alertBody = [eventText text];
        // Set the action button
    localNotif.alertAction = @"View";
 
    localNotif.soundName = UILocalNotificationDefaultSoundName;
    localNotif.applicationIconBadgeNumber = 1;
 
        // Specify custom data for the notification
    NSDictionary *infoDict = [NSDictionary dictionaryWithObject:@"someValue" forKey:@"someKey"];
    localNotif.userInfo = infoDict;
 
        // Schedule the notification
    [[UIApplication sharedApplication] scheduleLocalNotification:localNotif];
    [localNotif release];
 
        [self.tableview reloadData];
}

So, most of the explanation is in the comments.  I’ll talk you through some of the less obvious stuff.  The first tricky area is dealing with the NSCalendar.  We just use the NSCalendar object to break up the date into components.  Note: This demo does not require that we break the date up into components.  You could have just as easily fed the date from the date picker into the notification fireDate.  The reason that I’m showing you how to break it down is, you may have some sort of custom date logic to work with and this makes things much easier in the future.

Another important bit of code is where we set the alertBody or the notification.  In this example we set it to the text that the user entered into the text field.  You can set this to whatever you like.

The other thing I want to mention is the infoDict in the code.  This dictionary is your chance to associate some additional information with the alert.  For example, if you are using this alert in a game like We Rule to notify you when a crop is ready.  You might want to set a key and value that contains the id of the crop that has completed.  For now, we just set some arbitrary values and you can ignore them if you like.

After actually scheduling the notification, we just reload the tableview to get it to display immediately.

5. Handling Notifications After They Fire

The last piece of this puzzle is determining what to do when a notification fires.  Fortunately, this step is very easy and handled inside of the appDelegate.  When a notification fires, there are one of two situations. 1. The app is running and 2. The app is not running (or running in the “background”) .

Open up your app delegate .m file and add the following code.

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
 
    // Override point for customization after application launch.
 
    // Add the view controller's view to the window and display.
    [window addSubview:viewController.view];
    [window makeKeyAndVisible];
 
    application.applicationIconBadgeNumber = 0;
 
    // Handle launching from a notification
    UILocalNotification *localNotif =
    [launchOptions objectForKey:UIApplicationLaunchOptionsLocalNotificationKey];
    if (localNotif) {
        NSLog(@"Recieved Notification %@",localNotif);
    }
 
    return YES;
}
 
- (void)application:(UIApplication *)app didReceiveLocalNotification:(UILocalNotification *)notif {
    // Handle the notificaton when the app is running
    NSLog(@"Recieved Notification %@",notif);
}

The first thing we see here is the application badge number is getting set to 0.  Whenever a notification fires, it will increase the badge count on the application.  Next, we handle the case when the application launches from a notification.   This happens when the user presses the view button on the notification.  For now, we just NSLog the data, but you should handle the notification how you see fit for your app.

Finally, we implement the didReceiveLocalNotification method.  This method is required if you want to handle notifications at all in your app.  You will see this method fire when the app is running and you receive a local notification.  When the app is running, you will not see the UIAlertView show up with the notification data.

And there you have it!  The complete lifecycle of a local notification.  You may download the source for this tutorial below.  If you have any questions, feel free to post them in the comments section or .

Notifier Source Code

  • Djeef

    Okay great, throw an event at a certain time. Can I throw an event when my device is at a certain place??

  • http://icode.dreamvision-soft.com/blog/?p=72 iPhone Programming Tutorial – Local Notifications | iCode

    [...] Original post on iCodeBlog [...]

  • http://blog.webinnovations.pl/lokalne-notyfikacje-w-ios-4/ Lokalne notyfikacje w iOS 4 « WebInnovations

    [...] w którym wytłumaczona jest cała procedura przygotowania aplikacji z tego typu wiadomościami: iPhone Programming Tutorial – Local Notifications. Zakres tematyczny: Bez kategorii Dodaj komentarz Komentarze (0) Trackbacks (0) ( subskrybuj [...]

  • Axit Patel

    Awesome tutorial!

  • Liferoxx

    Awesome Tut!

  • http://maniacdev.com/2010/07/push-out-notifications-locally/ How To Push Out Notifications Locally Based On Time | iPhone iOS 4 iPad SDK Development Tutorials, Programming Tips, News

    [...] Brandon Treb has written a great step-by-step tutorial on exactly how to do just that, which you can find here: iPhone Programming Tutorial Notifications [...]

  • http://ajfek.pl Pawel Kata

    Why not try throwing an event at a certain time which would check for certain place and then reacted?

  • http://www.nkyea.com Isaac Ameyaw

    Great tutorial. Love it.

  • umer sufyan

    Great Tutorial!!! how can i launch my application automatically with out the help of Alert view that launches when the notification fires locally…
    And one more question How can i play a continuos sound in the background when the notification arrives….

  • http://brandontreb.com brandontreb

    @umer

    1. “how can i launch my application automatically with out the help of Alert view that launches when the notification fires locally…”

    You Can’t. The only options for launch are push, local, or custom url and the user opening from a link. There is no ‘automatic’ way to open your app

    2. “And one more question How can i play a continuos sound in the background when the notification arrives….”

    You can’t. The audio that plays when a notification arrives is limited to 30 seconds.

  • http://brandontreb.com brandontreb

    This is actually a much different problem to solve. There are only two possible solutions that I could see.

    1. Require the user to keep you application open while periodically checking for location. Then you could fire the event when they were within range.

    2. Read up on background processing and subscribe to the location based background processing protocol. Then have the device periodically send the location of the user to a server. Once the server detects that the user is in range, send them a push notification to open the app.

    I know that Solution 2 seems pretty complex, however it is most likely your best bet as Apple has this process fairly locked down.

  • Beau

    It Comes up with (error:’UILocalNotification’ undeclared (first use in function)) Why?

  • PowerPuff

    Great tut!
    Could you also provide a tutorial for push notification ;) It would be highly beneficial for people like me.

  • Beau

    Oh… I’ve only got SDK 3.1.3

  • http://brandontreb.com brandontreb

    Are you building your project for 4.0?

  • http://brandontreb.com brandontreb

    Maybe in the future. You can purchase my book which has a full chapter on push notifications… :)

  • http://www.tour2kerala.tk/ safil sunny

    i have a Doubt
    Is it Only Working In IOS4?
    I heard Multi threading can only done in IOS4

    regards
    safil sunny

  • http://brandontreb.com brandontreb

    @safil

    1. Local notifications are only supported by iOS4
    2. You are thinking of multitasking, multithreading has been supported on every device since release :)

  • http://www.qhud.net Qhud

    great tut, love it :D , it really is helping me out with my iphone programming,

    ~newbie here :p~

  • Abel

    Hi, Great tutorial. But how would i change the timezone on the table view detailtextlabel?

  • http://www.imobdevtech.com Jignesh Brahmkhatri

    I have a serious issue in handling local notification, I scheduled 3 local notifications each 1 minute duration, and I locked my device for 5 minutes, I received all three local notifications in locked state, but I am able to handle only last local notification i.e. I can get control inside didReceiveLocalNotification event but for other two, popup generated but it did not go to didReceiveLocalNotification.

    Please Help Me, I am stuck in it.

    Thanks In Advance.

  • Kobi Snir

    Great Tutorial!!!

    How can i set the local push to repeat every week?

  • http://christinemorris.com Christine

    Hey great tutorial, I wondered the same thing, can this be made repeat weekly? Thx.

  • Kendall Gelner

    There’s already a background API that can wake up your app when it changes locations, either continuously via GPS (very expensive battery wise) or when you switch between cell towers (which means you’ll get notified much less often).

  • Andrea

    Nice tutorial!
    I would like to do one thing in my app: setting the app’s badge to zero automatically every midnight (without opening the app). Is this possible?

    Thank you.

  • http://iphone-game-development.tumblr.com iphone app development

    Thanks you very much for sharing this tutorial about this Iphone application development. fresher can learn understand to see this video and this content.

  • PhiPhi

    From UILocalNotification.h:
    @property(nonatomic,copy) NSDictionary *userInfo;

    so after we set localNotif.userInfo = infoDict; in – (IBAction) scheduleAlarm:(id) sender shouldn’t we release userInfo?

  • PhiPhi

    Also I’ve downloaded the project from this page and ran it in a XCode 3.2.3 with SDK4, no errors but to me:
    [[UIApplication sharedApplication] scheduleLocalNotification:localNotif];
    does not seems to add any notification… not sure why.
    Querying UIApplication states that it can respond to that message.

  • PhiPhi

    Disregard this, sorry i was accepting the default datetime… in the past.

  • PhiPhi

    Also, to nag a bit more :) I don’t understand this:

    localNotif.applicationIconBadgeNumber = 1;

    It would intuitive for me for the iPhone to increase the current app’s badge number by 1 and not, like it’s actually doing it, setting it to 1.
    So in this example how can we actually increase it by 1 everytime a LocalNotification fires up?
    We can’t do localNotif.applicationIconBadgeNumber = 1 + [[UIApplication sharedApplication] applicationIconBadgeCount]; cause if, for instance, if I start from 0 and schedule 2 notification they will both encapsulate 1 and so the badge icon will not show 2 after they’re both fired.

    Any ideas?

  • frank

    how do I get to the beginning of the iPhone tutorials?

  • Iphone

    I do have a problem with this app i’ve create the interface added all the code but when i try to save any notification it just doesn’t anything. can someone help me pls?? i’m using xcode_3.2.3_and_ios_sdk_4.0.2.

  • http://brandontreb.com brandontreb

    Great question, I’m not 100% sure. I know you can pass an NSCalendar for the repeate. I will grok the documentation a bit more and get back to that.

  • http://brandontreb.com brandontreb

    No, the app must run to set the badge count to 0.

  • http://brandontreb.com brandontreb

    No, I’m sure inside of Apple’s UILocalNotification.m’s dealloc method, they release their userInfo dict. You are never responsible for releasing properties of Apple’s classes (unless YOU retain them).

    Also the dictionary that we pass to userInfo (infoDict) is an auto-release object since we initialized it with [NSDictionary dictionaryWithObject:], so we don’t need to release it either.

  • http://brandontreb.com brandontreb

    Sure, you can handle this however you want. I was just providing a simple example. You can modify the badge count however you like to fit your application’s needs.

  • shuifengxi

    Great tutorial!Thanks!I love it and I love you very very very much!
    But I have a question.How can I remove(delete) a local notification.
    May you tell me the code.Or add a action of “delete a saved notification” in your tutorial?

  • Noah

    I did everything right, and checked with the source code, but the table doesn’t actually show the notification! They occur, but not shown in the cell. How can I add this feature (like in your screenshot)?

  • Marcel

    Hello ,

    Thank very much for this tutorial .
    I have a question : how should i handle for example an event ( alarm ) to be lunched evert Monday and every Tuesday . I know how to handle a repeat Local Notification , but what i’m wondering if there is a possibility to handle with only one Local Notifications different types of repeating mode .
    If it’s not possible with just only one , i have to make create from 1 to 6 types of local notifications just for one reminder ( alarm ) .

    Thanks in advance

  • Mark

    Hello,

    Very very good tutorial, but unfortunately the program is unable to build due to the first file, “NotifierViewController.h”.

    The error given was “Expected identifier or ‘(‘ before ‘&’ token”

    I’m a newbie here so I guess i’m not familiar with what I’ve went wrong. I’ve checked/copy past the whole code a few times and i still can’t manage to find out my error. Help would be greatly appreciated!! :D

  • Ramkumar

    Hi your tutorial helped me a lot…But i need to know whether we can Auto send SMS when the local notification fires…..Please give me your suggestions..I want to send SMS automatically at particular date and time…

  • Alexey

    Great tut!!! Can i to play some sound from my app? How i can did it?

  • samba

    Brilliant tutorial brandontreb…..thanks a lot!!! I have tested out the code and it work beautifully.

    I do have a question for you though. Is it possible to push a local notification any time an entry is added to a log file? Could you point me to some useful websites?

    Thanks a mill,
    samba

  • Beirut

    Thank you for the great tutorial.

    May I ask, this line: NSArray *notificationArray = [[UIApplication sharedApplication] scheduledLocalNotifications]; is not causing a memory leak?

  • Beirut

    Ok, replying to myself here. It’s not a memory leak, as you don’t own the object. (right? right.)

  • Robbie

    Hi,

    I REALLY need to get this working… I downloaded the source and built and ran it but when I hit save it doesn’t save anything and there are no popups.

    what is wrong im using iphone 4.2 sdk

  • http://www.my-digital-source.ca Johnathon

    Do you have to jailbreak your iPhone for this to work?

  • Brendan

    Actually this is a problem in UILocalNotification. The system should automatically increment the badgeNumber when the notification fires. Then in your app, you can decrement the count if the user views the information that caused the notification to fire. If the user taps Cancel to skip the notification and then another one comes in at a later time, the notification should increment by 1 and the badge number should be 2. Then if you accept the notification, your app would decrement the badge number by 1 leaving it back at 1. But that’s not the way it works. When you schedule a notification, it’s the notification that determines what the badge count will be.

    Now, imagine you schedule 5 notifications, one after the other, incrementing the badge number by one for each notification. But the 5th one has a fire date set to fire before the first one. The badge number will display the number 5 even though only a single notification has fired. That’s because the notification’s badge number gets copied to the application’s badge number. This is wrong. What should be happening is you could set the notification’s badge number to 1 all you want and each time any notification fires, the application’s badge number is INCREMENTED by that number, not REPLACED by that number.