Sie sind auf Seite 1von 9

DOCUMENTATION

GET EARLY ACCESS

How we leverage iOS push notifications


Klemen Verdnik, December 19. 2013

Push notifications are a way for an app to send information to the users phone even when the app isnt
in use. From the users perspective this is an extremely valuable feature. It gets information delivered,
and does it in near real time. From the developers 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 Apples
Push Notifications in LayerKit.

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


First things first. What is LayerKit? Its 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 theres any kind of message-related activity (LayerKit receiving or sending
messages, or updating their mutable fields) this synchronization procedure is at work. And boy, theres a
lot if it.
We use a custom protocol (which well 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).

TCP socket and LayerKit

Its 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.
Apples 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
devices 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, its 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.

TCP socket and LayerKit

Apples 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 iOSs 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 i. from within the app.

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.

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 Apples documentation can be a tad confusing at times.
Lets 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) youd think you need to enable the last two modes, but lets think about this first.

Background modes

There is no detailed information in this screen about these modes, so heres what may be going through
your head: if I want my application to receive remote notifications, its 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,
theres a bit more information on these modes available when looking at your applications info.plist file.
Still, it doesnt provide accurate information.

'info.plist' file

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 users app usage. It learns the users 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 lets not get our hopes up
too soon, because theres 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; its where a keepalive handler can be scheduled only once every 600

i. available only for apps that are registered as VoIP apps; its 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 arent pushed into the devices notification center.

Let's go over a few states


Foreground state [G]

App running in foreground state

This timeline demonstrates a perfect scenario, where an app is launched and kept in the foreground
state (state G). We dont need to utilize the remote notification service, since we have a persistent
connection to the Layers 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!

App going into active-b ackground state

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 Layers 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.

App going into inactive-b ackground state


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

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


Lets 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.

Dispatching visual remote notifications

This timeline displays how the Layer server tries to trigger a synchronization request, but doesnt 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 dont 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 doesnt matter if theyre received separately or in a
bunch. The user reads one of those messages on another device, so the message gets marked as
read. Surely wed want the messages that were marked as read to disappear from the notification
center.
If those messages are pushed to the users 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.

Dispatching visual remote notifications and then cancelling them all at once

This is achieved by dispatching a remote notification with a badge value set to zero:
{aps: {badge: 0}}
Now lets see what we can do with local notifications:

Presenting local notifications and then cancelling a specific one

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 identifiersi.,
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.
i. we can add any kind of information to local notifications by utilizing the userInfo dictionary.

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 users
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 doesnt work with the 'contentavailable' flag), and another (lowest) which dispatches the notification when its best (to preserve
devices 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 (its 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:

Das könnte Ihnen auch gefallen