Frameworks • iOS • 36:06
Push notifications keep users up-to-date when their apps have data waiting for them on remote servers. Local notifications let apps generate notifications locally, without relying on a remote server. Learn best practices for creating notifications that get a user's attention through any combination of an alert message, a distinctive sound, or number badging of the application—even when your app isn't running.
Speakers: Jacob Farkas, Darryl Bleau, Chris Marcellino
Unlisted on Apple Developer site
Downloads from Apple
Transcript
This transcript has potential transcription errors. We are working on an improved version.
I'm Jacob and I'm going to be joined by my colleagues Chris and Darryl to talk to you today about implementing push and local notifications. So there's a bit of dilemma with iOS apps here. Your app can't always be running when you need it to run but you might have an important message that you need to get to the user.
For example, your game might want to tell the user that there're crops ready on their farm that they need to pick; or, I know you guys all love that game, your baseball game is about to start and you want to watch it; or they might have gotten a message from their friend on your service. How do you get these messages to the user if your app isn't running?
The solution on iOS is to use notifications. There's three different styles of notifications on iOS. The first is that you can set a badge. The badge lets the user know that there's a little bit of data waiting for them in your app. The second style is an alert. You can pop up an alert or send a short message to the user. The third style is a sound. You can play a sound that will get the user's attention.
Note that one notification can contain any combination of these three styles of notifications, so you can play a sound and set a badge at the same time. We have two ways of creating notifications on iOS. In iPhone OS 3 we introduced push notifications. Push notifications are a way for your server to get a notification onto the device. It does this by talking to the APNs Apple Push Notification Servers who forward the message onto the phone through a connection that they keep open all the time. In iOS 4 we've introduced local notifications.
Local notifications are a way for you to create these same notifications but without the network connection. They're similar in a couple of ways. The first is that they look the same to the user no matter how you create them. If you create the notification through push or locally, they're going to appear the same. You have a badge, you can display an alert, or you can play a sound.
Also in both cases, the iPhone OS is acting on behalf of your app. Your app doesn't need to be running for a notification to happen. There are some differences between push and local notifications however. The biggest is that push notifications come from a server while local notifications are created by your app.
This means push notifications are much better suited for any sort of network-based service. You can keep the user connected to a service through push notifications even if your app isn't running. With local notifications they're scheduled so they're much better for any sort of time-based notification. Also local notifications can be set to repeat so they can occur every day or every week indefinitely into the future. With push notifications it's a single shot deal. The server sends one notification and it appears on the phone. So why would you want to use notifications? Well they're a great way to get the user's attention.
It's standard behavior on the OS and users are used to these. It's also a great way to improve the battery life. Since your app doesn't need to be running you aren't using up any CPU and the battery life improves. Finally, push notifications are a great way for your app to stay connected with a network service. Your app doesn't need to stay running or keep a network connection open to your servers. The phone iOS will do it on behalf of your app.
So now Darryl's going to come up and talk to you guys about how to implement push notifications on the phone.
Hi my name is Darryl. I'm one of the engineers that works on the service which allows you to send notifications from your server directly to an application running on the device.
So just a few things I'm going to talk about today. One of them is just to give you a brief overview of how the notification architecture works. After that I'm going to tell you a little bit about the things we've been working on and how you can make use of them to provide an even better experience for your users. So first of all: How the Push Notification System Works. Now if you're already using the Push Notification Service, this is just going to serve as a brief reacquaintance to how it works. If you haven't used it, then this will be a bit of an introduction for you.
So to make use of the service you require two things. You require a device with your application running on it and you require a server which will send notifications. Once you have those two things you're ready to make use of the Push Notification Service. So just any particular device you need to know that device is Token.
This Token is available to you by the APIs on the device. Once you have that token you would tell your server about it and when your server has a Payload ready to send, you combine the Token and Payload together into a notification and send it to the Push Notification Service.
From there, the service will route the notification as appropriate. Let's take a closer look at that communication between your server and the Push Notification Service. The protocol used here is a Binary Protocol and it consists of a couple of things. There's the Device Token, there's the Payload which is specified in JSON and there's the Command Identifier so in this case it's simply 0.
The prefix to the Token and the Payload are their respective lengths. This makes up the entirety of a notification. All right some considerations for sending notifications. First of all the system was designed entirely with performance in mind. As such the communication with the service is unidirectional. It's also a streaming protocol which means that you'll send multiple communications back to back over the same connection. It also features a store and forward mechanism such that notifications which are sent to devices not currently online will be redelivered when that device reconnects.
The system also presents some debugging challenges. And this is mostly due to the fact that the system terminates connections on invalid data. Let's take a bit of a look at how that becomes a challenge-- when your server establishes a connection with APNs and notifications flow over that connection.
Let's take a closer look at what is actually happening on your server. We can actually think about that as your service which communicates to the operating system running your service. When you have a notification ready to send, you're actually sending that to the operating system and it's up to the operating system to manage the particulars of that connection. If we have multiple notifications in flight and there is a problem with a notification, then the push service will simply terminate the connection.
This can lead us to an issue where we have an orphan notification and it's a challenge for your service to do something intelligent in that case. As such we have an enhanced Binary Interface which will provide you with increased feedback. Along the way we've preserved what's best. This is still a high performance protocol.
It maintains the same streaming capability but now we will provide you with concise feedback. This is not a pure request response system as that would impair performance. Rather we've designed this system to provide you a response only in the case of an air condition. So let's take a look at how that would work.
In the same case as we had before, your service has two notifications to send. We still send those to the operating system but if you'll notice, we are keeping track of a sliding window of notifications we have sent as well as we have an ID associated with each one. In this case to keep things simple we have ID 1 and 2.
Now when those notifications are sent if there's an issue with any particular notification, before disconnecting the connection, the Push Notification Service will send you a response which contains that notification along with a little bit of information to tell you what was wrong with the notification. From there, you can know that there was an issue with that particular notification but the notifications sent subsequent to it still need to be redelivered. We're going to talk a little bit about the Store and Forward mechanism.
As mentioned the Push Notification System accepts notifications for devices which are not currently online. And will store them for a limited amount of time and redeliver them when a device reconnects. However, this is potentially problematic as the information contained within any notification may be sensitive to time; and yet we will deliver it potentially after too much time has passed even perhaps after days after the information in the notification was useful. When you can imagine having, say, a restaurant application that lets you know when your table is ready, if you turn off your device it's not useful for you to receive information that your table is ready tomorrow.
And so with the Enhanced Binary Interface, you can now specify a maximum useful lifetime for any particular notification. And if a device reconnects after that expired period has passed, that notification will not be delivered. So note that we will always attempt to deliver a notification at least one time.
So you can make use of this for a fire and forget type notification by simply setting the date to a time in the past or to 0. Also note that while you are specifying a lifetime, you'll still not be able to exceed the maximum default expiry for the Push Notification System.
And some further considerations for the expiry, it's specified in seconds since epoch, this is the number of seconds since January 1, 1970. You can still make use of the existing message command which essentially is the same as using the command with the expiry and saying please store this as long as you can.
Also note that this system also expects time coordination on your server; so you want to make sure that you're making use of the Network Time Protocol so that your server time is in sync with the Apple Push Notification Server time; so putting that altogether the existing message command as we've looked at looks something like this. There's the Device Token, the Payload, the Command Identifier and the Prefix Links. To make use of the enhanced Binary Interface we change a few things.
First of all it's a new command ID and instead of 0 it's now 1. You will then add in your particular identifier and your expiry time. This makes up the entirety of the enhanced Binary Interface Protocol. The last thing I have to talk to you about today is receiving notifications over a Wi-Fi connection.
As some you have noticed, prior to iOS 4 devices which were connected by a Wi-Fi and sleeping resorted to a polling behavior in order to receive notifications. However, with iOS 4 we now fully support push notifications over Wi-Fi connections. This requires an iPod, iPad or iPhone 3GS or newer. So in summary we have the Enhanced Binary Interface which will alleviate some unknown disconnection issues for developers as well as allowing you control over the lifetime in any particular notification. Also all of this is available right now.
You can use this new message command as soon as you exit. As well we have Enhanced Device Support such that we now support push notifications over Wi-Fi with iOS 4 on iPod, iPad and iPhone 3GS or newer. All right that's all I have to talk to you about today. Back to Jacob to tell you more about local notifications.
[Jacob Farkas]
Great. Thanks Darryl. So now I'm going to talk to you guys about implementing local notifications in your app. Just to recap real quick local notifications are new to iOS 4. They're scheduled to occur at some point in the future and you can also mark them as repeating.
You can also cancel your local notification at any time. So when would you want to use a local notification? Let's go over some examples of apps that might use them. The first is a Alarm Clock app. You want an alarm to happen every morning at the same time. This is a great example of a repeating notification because you want it to happen every day of the week. Another example is a Reminder App. You want to be reminded to not forget something before a big trip.
The last example is a Location Based App. Using the new location services in iOS 4 you can provide location in the background. If your background app figures something out like your friend is near, it can use a local notification to display an alert and let the user know.
There are some times, however, when we don't want you to use local notifications. The biggest is for any sort of in-app errors or confirmation dialogues. For that it's best to use UIAlertView or just make your own feedback in your own UI. Also if you want to alert the user about a calendar-based event, we recommend that you look at using EventKit. It will allow you to create an event in the user's calendar and you can set an alarm on that event.
The user will get notified by the system when that event is about to occur. So now let's take a look at the API for UILocalNotification. If you remember there's three different styles of notifications-- a badge, an alert and a sound. So we have properties for each of those.
Once you've set that up you're going to want to schedule your notification to happen at some point in the future and you may also want to mark it as repeating. Notifications also provide a userInfo Dictionary so you can add any extra metadata that your app might need. So let's start by taking a look at the three different styles you can set on a notification.
The first is a badge. If you set the applicationIconBadgeNumber property when the notification fires, you'll get a badge on your icon. There is one trick to this though. If you set zero your badge won't get cleared. To clear your badge or to change it once your app is running you'll want to use UIApplications applicationIconBadge property.
The next style of notification is an alert. If you set an alert string as an alertBody in your notification, an alert will pop up with a single button. When the user taps that button the alert will be dismissed. You can also set actions on your alerts. What actions do is allow the user to tap on that extra button on the notification to launch directly into your app - just like that.
So there's one note here. Since you're displaying strings to the user, it's always a good idea to localize them. Even if your app isn't currently shipping in multiple countries, it will make your life a lot easier if you decide to do that in the future. We've made this really easy to do on iOS 4. Just create a localized strings file and add key value pairs of the strings that you want localized. When you make your alert use those keys as the content of the alert. Note that we are not localizing the strings right now.
This is important because if the user changes their language between now and when the alert fires, they'll get the right localized alert. The OS will localize it on behalf when the alert fires. The last property for alerts is a launch Image. This is new to iOS for both push and local notifications. So those of you that aren't even planning on doing local notifications might be interested in this.
When an alert fires and the user taps on that button, your app is going to get launched as if they had tapped on the icon from the home screen. This means you'll see the default launch image or you may see a screenshot of your app in its previous state. This might not always be what you want though. You might want to take the user directly into the thing you notified them for.
To do this, you set an alertLaunchImage. In the case of our example app here we're telling the user about a baseball game that they want to watch. If they hit the view button, we want to take them directly into the view where we're showing them the baseball game in progress.
So we take a screenshot of that view and we set that as our alertLaunchImage. When the user taps the button the app will be launched and the transition will be seamless between the application launching and your view coming up. Finally, the last style of notification you can create is a sound [glass chime].
You can play any sound in your apps bundle or if you don't have your own sound you can just play the system default sound. If the sound is working it sounds like this [background sound]. Cool. That's it for the different styles of local notifications. Now you're going to want to schedule that notification so let's look at how you do that. To schedule a notification you just set the fire date and time zone property of the notification. There's one catch though. Try not to do your own time-based math here.
This time tomorrow isn't necessarily going to be sixty seconds times 60 minutes times 24 hours from now. If say Daylight Savings Time happened tonight you'd end up an hour off tomorrow when your alert fired. Instead, you should use NSDateComponents to set the specific date you want the notification to fire at.
Then the OS can handle all the tricky time-based math for you. So I want to take a minute to step aside and talk about the different kinds of times that you can set a notification to fire at. This gets a little tricky but hopefully we can figure it out.
There are two different kinds of times. The first is what I'm going to call a Universal Time. A Universal Time happens at the same instant no matter where you are in the world. It doesn't have anything to do with the time zone you are in. A Wall Time is whatever is on the clock on the wall where you are right now. So let's look at a Universal Time first. To make a notification with the Universal Time, just set the date and no time zone.
When you do that the notification will happen at the same instant all around the world. A great example of this is a baseball game. Baseball games start at the same time whether you're in San Francisco or in New York so we just set an NSDate on this notification and when the notification fires the time in San Francisco may be 2PM but at the same instant it's going to be 5PM in New York. The other kind of time is a Wall Time. A Wall Time has a date and a time zone set on it.
This time is going to get changed depending on what time zone you move to. So an example of this is an Alarm Clock that wakes you up every morning. You always want that to happen at the same time so you set a date and a time zone on that notification. Now when you're in San Francisco for WWDC this week it happens at 9AM and when you move back to New York it still happens at 9AM.
So just to review that-- if you don't have a time zone it's a Universal Time. A couple examples of this are a Baseball game or a Conference call or an event like the close of the Stock Market. These all happen in a single instance regardless of what time zone you are in.
The other kind of time is a Wall Time. A couple examples of this are an Alarm Clock, or New Year's Eve or a TV show that happens at the same time every day. All right getting back to scheduling notifications. You can also mark your notifications to repeat at certain intervals. To do this just set the repeatInterval on your notification. You can use any NSCalendarUnit to do that.
So in this example we're using NSDayCalendarUnit and the notification will repeat every day. But you could also use NSWeekCalendarUnit; now it repeats every week. One thing to note here is the repeatCalendar that's also associated with it. Not all users use the Gregorian calendar, so if you're doing something that assumes that a year is a certain length, you want to also set the calendar that you're expecting otherwise you might get the wrong repeat for a user using a different calendar. So that's how you schedule a notification. The last property in a local notification is the userInfo.
The userInfo is just an NSDictionary that you can store any additional keys that your application might need. A couple examples of what you might want to put here an identifier for the notification or possibly some information for your app of what view it should launch into. You can put anything in here that can be stored in an NSDictionary.
All right so we now have a local notification and we want to get that to actually appear on the system. How do we do that? Well it's really simple. Just call schedule notification on UIApplication. There's also a matching cancel call. We've also provided a couple of convenience methods for you so you can get all your local notifications and you can cancel them all with one call.
Finally, UILocalNotifications implement the NSCoding Protocol so you can serialize them out to your database and use them later. Now let's look at the different cases where your notification fires. There are three different possible states for your application when the notification happens. You could be not running or suspended, your app could be in the foreground or you could be one of the special types of apps that runs in the background like a location or audio-based app.
So let's look at the first case where your app has never run or it's suspended. When the notification fires, iOS will handle that notification on your behalf. The notification will pop up and if the user decides to tap on that button and launch your app, your app will get brought to the foreground.
When your app launches you'll get the application:didFinishLaunchingWithOptionsCallback and the notification will be in there. Your app can then do whatever it needs to do to respond to that notification. The next case is an app that's already running in the foreground. In this case the notification will still come in but the iOS won't do anything on behalf of your app.
No alert will pop up or no badge will be set. Instead we know that your app is going to be smarter about displaying the appropriate UI or just going straight to the view that you need to go to. So in this case the notification comes in and your application receives application:didReceiveLocalNotification.
Your application can then do the appropriate thing. In this case we want to display a subtle little reminder about the baseball game that's going on. Finally, there's the third case where your app is running in the background. If you're one of the applications using the new multitasking API in iOS 4 you may be running in the background for location, Voice Over IP, or Audio. If you are one of these apps and something causes you to want to alert the user, you'll want to schedule a local notification with an action button. This is a good way for the user to be given the option to jump straight into your app.
So you can schedule a local notification now rather than scheduling it for in the future. This gives the user the ability to launch your app. Note however that if you aren't one of these special background apps, this probably isn't what you want. If you call scheduleLocalNotificationNow and you're already in the foreground, the system is just going to turn right around and call didReceiveLocalNotification and nothing's going to happen. All right so a quick word on alerts.
Be wise with your alerts. Don't bother the user. We'd hate for them to delete your app because you displayed too many alerts and you bothered them. With push alerts they can also just turn off push for your app and then you can't get any information to them so be nice to the users. If you can, use a badge instead of an alert. It still lets the user know that there's information there for them, but it's a little less intrusive. All right so now Chris is going to come up and give us a demo of what we just learned.
Hi I'm Chris Marcellino. I'm one of the iPhone iOS Software Engineers and I'm going to talk to you and show you a few tips about some UILocalNotification coding in your apps. If you were here last year you might remember that we showed you the Squawkr app and we showed you how to extend it to use push notifications. Well this year I'm going to show you how to extend it to use local notifications.
So Squawkr is your standard social networking app except it's an app that's all about complaining. As you see here I'm complaining about my bad day and my lunch and other things that occurred to me. I have some of my friends complaining about stuff going on in their lives too.
So I took the Squawkr app and I decided that I really wanted the ability to let users say they want to be reminded when to Squawk. So I added a button that brings up a view controller, lets the user pick just really simply how far in the future they want to be reminded.
And what I did is I wired up this Schedule button to the schedule IV action in the schedule view controller that runs to this code. So say once I click Schedule this action is going to get called and first I'm going to cancel all the existing notifications just because my app only wants one notification active at once. And I'm going to get the countdown duration from the date picker and then speed it up a little bit for purposes of this demo; and then finally I'm going to create my local notification. Now this local notification doesn't use any calendar.
It's just a simple time in the future. So I create a new NSDate with the time interval nSeconds in the future and set that as the fireDate on the UILocalNotification and then I get my localized strings from my alertBody and alertAction. And as Jacob talked about these are strings that are in my Localizable.strings file in my apps bundle. So here's my English strings and you of course could have strings for every language that you're using or supporting; and then I have a custom sound here that I want to set as my soundName so we hear that when the switch is on sound on the phone.
And finally I have a custom launch image because when I'm launching the app for the user to create a new Squawk I want to make sure the UI doesn't glitch that it goes straight from the right default PNG showing the creation UI seamlessly to the UI without showing the old snapshot beforehand.
And finally, I add in a little bit of user info to give me some context about what this local notification was for when the app's resuming. So I just put in an NS Boolean Bool:Yes with the createSquawk key and I'll look in a second. And of course I schedule it, release it and then dismiss view controller [sound effects]. Okay, so let's look at the application delegate of my application.
So in my SquawkrAppDelegate class, I have two methods I've added code for. I've added application didFinishLaunchingWithOptions, it's a delegate callback, your application gets on launch to implement it; and I also have application didReceiveLocalNotification. So let's say that I schedule a notification and quit the app [sound effects]. Now the app wasn't running as you saw that I terminated it in the App Switcher.
But here I launched the app it's going to launch fresh and the application didFinishLaunchingOptions delegate callback is going to get called. So in my application didFinishLaunchingWithOptions delegate callback, what you see here is that I get the local notification out of the launch dictionary, and I check to see if the userInfo key has my createSquawk key. If it does then I'm going to go straight to my Squawk creation UI.
And you'll notice that you saw nothing but a seamless transition from my default PNG to my Squawk UI. Now let me show you another case. If, for example, instead of launching fresh, my application is running if it supports multitasking it's linked on iOS 4.0, when I schedule a notification and spin the app, once the Squawk fires [sound effect] and I resume the app, you're going to see that my application didReceiveLocalNotification delegate callback is going to get called. And I'm stuck here in the debugger we're broken, this is the default PNG that looks just like my UI that I'm going to launch to. But that's actually the image itself-- not real UI.
I took a screenshot of my app when I was developing it. So here I am in this delegate callback didReceiveLocalNotification, and one thing that you may have encountered if you've already started using local notifications in the developer seed, is that you want to know sometimes if your app is structured this way whether you're resuming in response to a local notification or if you've just received a local notification while your app was already foregrounded. The way to tell apart those two situations is to call the UIApplication ApplicationState property and get the current application state.
So the application state has three return values that you can get. You can get active, inactive or background; and the only time you're going to get this delegate notification call is when either you're active already or you're inactive. Now if you get this callback and application state returns inactive, that means the user launched your app with the action button in your notification and the application is resuming a response to the notification.
So if your UI was to normally maybe ask user confirmation while the app was already foregrounded you might not want to do that if the app is resuming because the user already acknowledged your notification and indicated they want to launch your app you go straight to that UI.
So that's how you can tell the difference between those two cases. Again I checked my createSquawk key in case I had multiple notifications and wanted to differentiate between each of them; and in this case when I have state UIApplicationStateInactive and I have shouldSquawk, we launched right to the Squawk UI. As you see here I'm right in the Squawk UI and I can create a new Squawk.
So another thing I'd like to note is if you receive a notification while your application is already running foregrounded, one thing you should do is not just if you can help it display a UIAlertView. Try to do something custom like take the user to the right part of your application or have your existing custom UI reflect the fact that local notification fired.
So one other thing you may have noticed is that when I cancelled all notifications here I wasn't really specific about which one I wanted to cancel or anything. But if you do start cancelling specific notifications or maybe you have one of those background apps that's using notification services or audio, and you're targeting certain notifications to cancel, one thing you should keep in mind is that if a notification alert is already up on screen and you cancel it, iOS will tear down the existing already visible notification alert so the user will kind of maybe wonder why it disappeared.
This might be useful if you have notification data that's no longer valid and you want to actually make it go away and maybe replace it with a new one. But if you don't and say the notifications just fired, don't bother cancelling it as the operating system will cancel it for you.
The final note I want to make on UILocalNotification is that as Jacob mentioned it's for copying and coding. Coding gives you a lot of power in that your application can serialize instances of UILocalNotification using NSKeyArchiver and NSKeyUnarchiver and store those in any existing data model you might have like a core disk or a database on disk.
When you restore those local notifications when you re-create them using Key Unarchiver, you can compare them using is equal to the existing ones you have running via scheduled local notifications or any other ones you might have in your model or even make copies of those and change them to suit your needs.
One consequence of this is that UILocalNotification implements is equal on hatch in the deep sense. Two different instances of UILocalNotification will be equal if all of their properties are the same so you can use that to compare an instance you may have gotten from the system versus one you had in your data store or one you created by hand to see if they're equal. Equivalently you can ask the system to cancel notifications where you've unarchived an instance from your date store.
If you change any property of the notification, you're going to find your notifications are no longer equal. And of course the system implementing has correctly on UILocalNotification means you can use your own dictionaries and your own sets to be able to correlate and find your notifications. That's all I have for you today. I'm going to hand it back to Jacob for more on local notifications.
[Jacob Farkas]
All right thanks Chris. So in summary, local notifications and push notifications are great ways to get the user's attention. It gives you all the functionality of being a background app without you actually having to use any CPU. This saves on battery life and users like it. If you want to stay connected with a network service, think about using push notifications. Your server can send messages to the phone. If you want to notify the user about something that's time-based, use local notifications.
Remember to always localize. It will save you a lot of work if you ever decide to ship in other countries. Also, remember the difference between Universal Times and Wall Times. notifications can fire at different instances depending on how you set them up. And finally, if your app is already in the foreground no UI is going to be shown on your behalf. You should handle that notification and do something appropriate for your app.
If you'd like any more information on this Mark Malone is our Evangelist. We also have information up on the forms. There's some related sessions you might be interested in; the Multitasking session will tell you all about being a background app which you can use local notifications with; and there's an EventKit Session later today.