layer - blog - how we leverage ios push notifications

9
DOCUMENTATION GET EARLY ACCESS Push notifications are a way for an app to send information to the user’s phone even when the app isn’t in use. From the user’s perspective this is an extremely valuable feature. It gets information delivered, and does it in near real time. From the developer’s perspective, however, using APNS (Apple Push Notification Service) can sometimes be a bit limiting and clumsy. And with the release of the iOS 7, developers can take advantage of the new push features, but will inevitably stumble upon new challenges. Here at Layer we're building the open communications layer for the Internet. Through a simple yet powerful SDK, Layer provides the building blocks for developers to add secure and reliable messaging, voice and video features to any app. Layer provides a globally-distributed, ultra-reliable infrastructure and handles the hard parts - sync, message states, push notifications and more - so that developers can focus on innovation and delighting their users. Because at Layer we deliver messages in real-time, even when the app is not active, it's important that we're able to take the advantage of APNS. Allow me to share my findings and how we tackled Apple’s Push Notifications in LayerKit. I'd like my data synced, and to go please First things first. What is LayerKit? It’s what we proudly call our iOS SDK here at Layer. Keeping data synced between the app and the server at all times (or at least as much as possible) is a non trivial task for an iOS developer. We like to think LayerKit is pretty good at it. Our approach uses a special synchronization procedure, which can be triggered internally (within the LayerKit client) or externally (by the server). So whenever there’s any kind of message-related activity (LayerKit receiving or sending messages, or updating their mutable fields) this synchronization procedure is at work. And boy, there’s a lot if it. We use a custom protocol (which we’ll discuss in a future article) for transferring synchronization data over a persistent TCP connection. Besides that, the protocol also enables receiving of special incoming commands from the server, which trigger the synchronization procedure inside the LayerKit at any time (for example, in response to an incoming message). How we leverage iOS push notifications Klemen Verdnik, December 19. 2013

Upload: neokrish

Post on 09-Dec-2015

16 views

Category:

Documents


1 download

DESCRIPTION

iOS Push Notifications

TRANSCRIPT

Page 1: Layer - Blog - How We Leverage IOS Push Notifications

DOCUMENTATION GET EARLY ACCESS

Push notifications are a way for an app to send information to the user’s phone even when the app isn’t

in use. From the user’s perspective this is an extremely valuable feature. It gets information delivered,

and does it in near real time. From the developer’s perspective, however, using APNS (Apple Push

Notification Service) can sometimes be a bit limiting and clumsy. And with the release of the iOS 7,

developers can take advantage of the new push features, but will inevitably stumble upon new

challenges.

Here at Layer we're building the open communications layer for the Internet. Through a simple yet

powerful SDK, Layer provides the building blocks for developers to add secure and reliable messaging,

voice and video features to any app. Layer provides a globally-distributed, ultra-reliable infrastructure

and handles the hard parts - sync, message states, push notifications and more - so that developers can

focus on innovation and delighting their users.

Because at Layer we deliver messages in real-time, even when the app is not active, it's important that

we're able to take the advantage of APNS. Allow me to share my findings and how we tackled Apple’s

Push Notifications in LayerKit.

I'd like my data synced, and to go please

First things first. What is LayerKit? It’s what we proudly call our iOS SDK here at Layer. Keeping data

synced between the app and the server at all times (or at least as much as possible) is a non trivial task

for an iOS developer. We like to think LayerKit is pretty good at it. Our approach uses a special

synchronization procedure, which can be triggered internally (within the LayerKit client) or externally (by

the server). So whenever there’s any kind of message-related activity (LayerKit receiving or sending

messages, or updating their mutable fields) this synchronization procedure is at work. And boy, there’s a

lot if it.

We use a custom protocol (which we’ll discuss in a future article) for transferring synchronization data

over a persistent TCP connection. Besides that, the protocol also enables receiving of special incoming

commands from the server, which trigger the synchronization procedure inside the LayerKit at any time

(for example, in response to an incoming message).

How we leverage iOS push notificationsKlemen Verdnik, December 19. 2013

Page 2: Layer - Blog - How We Leverage IOS Push Notifications

It’s all sunshine and rainbows when our app runs in the foreground and has a working connection to the

Internet. This is when we have full control (programmatically speaking) over our application and the open

TCP socket. In this state LayerKit is always up to date with its messages. But once the app goes into a

background state (user switches to another app or locks the screen), things get a bit complicated.

Apple’s Remote Notification Service has been available since iOS 3.0, and we can make great use of it

by having our server dispatch remote notifications with short messages. They get pushed inside our iOS

device’s notification center and the user can respond by swiping/tapping the notifications. Those

particular actions launch the app, as well as notify it that user responded to a certain notification.

However, it’s not until the app is fully up and running (in the foreground), establishes a connection to

Layer services, and finally completes the synchronization procedure, that it can be considered up to

date.

Apple’s policy does allow for an app to run in a background mode, but unfortunately each mode comes

with many trade-offs. One such mode, which is fairly easy to implement, is this: the app is allowed to

behave in the background in much the same way as would in the foreground - but it is limited to 10

minutes (or less, depending on iOS’s mood). In this state, we can keep the TCP socket open, trigger the

synchronization procedure from the server, and, if we want to alert the user about incoming messages,

we can dispatch local notifications from within the app.

TCP socket and LayerKit

TCP socket and LayerKit

i.

Page 3: Layer - Blog - How We Leverage IOS Push Notifications

Background fetch on remote notification

iOS provides several other background modes that can wake up or keep an app running in a

background state, but only if your app uses of any of the following features: Audio and AirPlay, Location

updates, Newstand downloads, Voice over IP, External accessory communication, Background fetch,

etc… and iOS 7.0 also brings one more addition to this palette: background fetch on remote notification.

In LayerKit, we focus on two kinds of push notifications:

push notification - which is a remote notification with an alert string or body

example payload: {aps: {alert: "hey there, how you doing?"}}

push notification with background fetch - which is a remote notification with content-available

flag, newly introduced in iOS 7

example payload: {aps: {content-available: 1}}

Implementing this is not so straightforward, as Apple’s documentation can be a tad confusing at times.

Let’s say you wanted to make a test playground app where you could trigger a background fetch upon a

received remote notification with a content-available flag. By looking at the XCode project settings

(capabilities section) you’d think you need to enable the last two modes, but let’s think about this first.

Local notifications and LayerKit

i. Why local notifications? Because of greater control. We can cancel each individual dispatched local notification, whereas with

remote notifications we can only cancel all at once.

Page 4: Layer - Blog - How We Leverage IOS Push Notifications

There is no detailed information in this screen about these modes, so here’s what may be going through

your head: “if I want my application to receive remote notifications, it’s enough to register for them in the

code by calling the registerForRemoteNotificationTypes: method. This way my app will be

able to receive remote notifications when I implement the

application:didReceiveRemoteNotification: delegate method.”

But what are the last two background modes for, anyway? You might think the last one, “Remote

notification,” is for receiving plain remote notifications, but why would you need to enable a feature that

already works? And the second to last mode, “Background fetch” will let you wake the app up with the

'content-available' flag. Right? <Buzz!!> Wrong!

Both modes are indeed background modes, but they really should be named differently. Fortunately,

there’s a bit more information on these modes available when looking at your application’s info.plist file.

Still, it doesn’t provide accurate information.

Background modes

'info.plist' file

Page 5: Layer - Blog - How We Leverage IOS Push Notifications

Here's what these two modes really provide:

The first one, called Background fetch (“Item 0” : “fetch”), is a background mode where a smart

scheduler wakes your app based on the user’s app usage. It learns the user’s behavior. For example:

when and how often does the user actually use the app.

The last one, called Remote notification (“Item 1” : “remote-notification”), is a background mode

where the app is woken up upon receiving a remote notification with a “content-available” key

defined inside the ‘aps’ payload.

Great, we now have a way of waking up the app remotely from the server. But let’s not get our hopes up

too soon, because there’s bound to be some trade-offs, right?

When the application is woken up remotely, LayerKit only has a 30-second window to complete the

synchronization procedure.

If the user rebooted his device and never launched the app since the reboot, the app will never wake

up remotely.

If the user killed the app manually from the app switcher, the app will also never wake up remotely.

Leveraging remote and local notifications

We want to keep the LayerKit client accessible from the server as much as possible since we want to

keep it in an up-to-date state as frequently as possible. But we must also be aware of every possible

state the app can be in. Here is a checklist of states that are relevant for LayerKit:

i. available only for apps that are registered as VoIP apps; it’s where a keepalive handler can be scheduled only once every 600

Page 6: Layer - Blog - How We Leverage IOS Push Notifications

Let's go over a few states

Foreground state [G]

This timeline demonstrates a perfect scenario, where an app is launched and kept in the foreground

state (state G). We don’t need to utilize the remote notification service, since we have a persistent

connection to the Layer’s server, and synchronization procedure can be triggered at any time.

But keep in mind: as soon as LayerKit establishes a connection with the Layer Cloud server, it

automatically starts the synchronization procedure.

Active background state [F]

As soon as the user presses the home button or locks the device, the app goes into a background state.

We can utilize the active-background state (F) and maintain our persistent TCP connection, but only for

10 minutes!

i. available only for apps that are registered as VoIP apps; it’s where a keepalive handler can be scheduled only once every 600

seconds, and it can only run for 10 seconds

ii. app can run for about 10 minutes in an active background state.

iii. app can receive remote notifications, but alerts aren’t pushed into the device’s notification center.

App running in foreground state

App going into active-background state

Page 7: Layer - Blog - How We Leverage IOS Push Notifications

When the synchronization procedure is complete, we can notify the user of incoming messages by

dispatching local notifications.

Inactive background state

After the 10 minute interval, LayerKit drops the connection to Layer’s server, and the application goes

into an inactive background (state C). The server can trigger a synchronization request with the help of a

remote notification that can set off a background fetch inside the app. The remote notification must have

the content-available flag included in the ‘aps’ payload. These notifications are sometimes described as

silent remote notifications.

After the Layer server dispatches the remote notification, it gives the app some time (marked with

grey/white dashed lines) to connect to a Layer server. iOS wakes the app up and lets it perform a

background fetch for 30 seconds (marked with red/white dashed lines). That should be enough for

synchronization procedure to complete. Keep in mind that while the fetch process is in the background

we can dispatch local notifications once the synchronization procedure is complete.

App killed or never launched after reboot

Let’s think about the background fetch trade-offs we discussed earlier. If the user killed the app, or if he

never launched the app after a device reboot, the app will not wake up in response to a remote

notification with 'content-available' flag.

App going into inactive-background state

Note that in the timeline, the length of some states are exaggerated for the sake of diagram aesthetics.

Dispatching visual remote notifications

Page 8: Layer - Blog - How We Leverage IOS Push Notifications

This timeline displays how the Layer server tries to trigger a synchronization request, but doesn’t get a

connection back from the app in a given period (marked by grey/white dashed lines). After this period,

the server marks that app as unreachable, and only dispatches a visual remote notification with an alert

text from that moment on. Or at least until the app reconnects to the server.

Note that we always send the content-available flag in the remote notification payload, even if the app

failed to respond in any of the previous attempts. One of the scenarios might be that the user launched

the app while in a no-cellular or no-WiFi coverage area. As soon as his iOS device finally connects to

the Internet, it will receive the push notification and, consequently, wake the app up.

Remote vs local notifications

You might be wondering, why don’t we just settle with remote notifications? Why even bother dispatching

local notifications from the app? The answer is simply: we have much better control over local

notifications than remote ones.

Suppose the user receives three messages. It doesn’t matter if they’re received separately or in a

bunch. The user reads one of those messages on another device, so the message gets marked as

read. Surely we’d want the messages that were marked as read to disappear from the notification

center.

If those messages are pushed to the user’s device in a form of remote notifications with text, there is no

way to cancel individual ones from the notification center. The server can only cancel all remote

notifications at once.

This is achieved by dispatching a remote notification with a badge value set to zero:

{aps: {badge: 0}}

Now let’s see what we can do with local notifications:

Dispatching visual remote notifications and then cancelling them all at once

Page 9: Layer - Blog - How We Leverage IOS Push Notifications

1. an app in the background receives a sync-request,

2. after the synchronization process, it receives three unread messages,

3. app presents three different local notifications and marks them with special identifiers ,

4. app receives another sync request,

5. after the synchronization process completes, the app notices one of the messages has been marked

as read, so it removes it from the notification center.

There are some other controls we have on the server-side over remote notifications. We can specify an

expiration time for each notification - how long the message should exist in APNS’ queue - if the user’s

device is unreachable. One other thing you can specify is priority. APNS can deal with two kinds of

priorities, one which dispatches the notification immediately (but doesn’t work with the 'content-

available' flag), and another (lowest) which dispatches the notification when it’s best (to preserve

device’s battery).

Conclusion

Apple's notification system is a very powerful tool. It comes with a robust but somewhat confusing API.

As a developer, you can solve a lot of real-time notification scenarios with it, but only if you're able to

figure out how to leverage it properly and can afford to support it (it’s an ever-changing territory). At

Layer, we believe application developers shouldn't be wasting time solving the problems described

above. Reinventing the wheel over and over again takes time away that can be better spent. We want

application developers to focus on making apps with great design and polished user experiences.

Secure and reliable messaging, voice and video features - we got that covered.

Want to help us build the open communications layer for the internet? Join us.

Share:

Presenting local notifications and then cancelling a specific one

i.

i. we can add any kind of information to local notifications by utilizing the userInfo dictionary.