We have just published a full implementation for providing Push Notifications (APS) support for iPhone clients using RemObjects SDK, with minimal work required.
The current implementation is provided for servers written in .NET, although it should be easily adaptable to serve as a reference for implementing a similar functionality for RO/Delphi.
What are Push Notifications?
To conserve battery power, as well as reduce CPU load and network traffic, iPhone does not allow applications to stay active in the background and keep communicating with servers. This makes traditional approaches for real-time notifications, such as RemObjects SDK's event sinks unfeasible, but is an understandable limitation given that users expect their iPhone batteries to last a long time and want to keep a tab on (sometimes metered) network traffic.
In exchange, Apple provides so called Apple Push Services (APS) that provide a unified approach for all applications on the device to receive notifications while they are not running.
Details about how APS works can be found in Apple's documentation in the iPhone SDK (link), but basically, the system works by iPhone applications registering a device token with their servers (that's the server you, the application author, is providing), and the server then dispatching notification messages through infrastructure provided by Apple. Apple's servers will consolidate notifications received from all the different sources (yours and others) and send them to the devices .
Notifications can include text messages (which will show on the device as alerts, similar to SMS), app icon badges (like the 'unread email count' in Mail) that will be attached to your application icon on the home screen, and sound alerts.
Beyond providing these notifications, and giving the option to launch into your application from the displayed alert message, APS does not affect the actual communication or flow of data between your application and its servers, and neither does our ApplePushProvider implementation. If the user launches your application, it is up to your own code to retrieve the latest data from your servers (for example to display data matching a badge count or alert message your server pushed down to the phone). RemObjects SDK (or Data Abstract) of course make this easy, but this functionality will be application-specific, so it is beyond the scope of what the ApplePushProvider classes do for you.
What does ApplePushProvider Do?
The classes provided by RemObejcts.SDK.ApplePushProvider basically provide three sets of functionality for you: Provide a service with a consistent interface that your iPhone client can use to register (and, if needed, unregister) devices for receiving notifications.
Provide a central "device manager" to keep track and persist information about registered devices on disk. A class that encapsulates the logic needed to talk to the APS servers in order to send out the actual notifications to one or more devices.
Optionally, this can all be tied in with your server's existing User Management, to link devices to your server's user accounts and provide user-specific notifications.
On the iPhone: Registering your Application for Push
Registering your iPhone application to receive Push Notifications is very straight forward, requiring only a handful of lines of code, in addition to the provisioning required to enable Push from Apple's side. Provisioning for Push is explained in detail in Apple's documentation, but basically involves the following steps:
- Creating a unique AppID for your application (wildcard AppIDs will not work for Push).
- Enabling that AppID for Push and creating and uploading a certificate to the iPhone Developer Portal (the certificate created here will later need to be exported to the machine running your server).
Once that is done, all that is left is to add the two PushProvider.m and PushProvider.h source files to your project (and link your project against libRemObejctsSDK.a), and add the following few lines of code to your AppDelegate:
#import "PushProvider.h"
#define URL @"http://yourserver.com"
- (void)applicationDidFinishLaunching:(UIApplication *)application
{
// ... existing code ...
// pick which of the three notification types your app wants to receive
[application registerForRemoteNotificationTypes:UIRemoteNotificationTypeBadge|UIRemoteNotificationTypeAlert];
}
- (void)application:(UIApplication *)application didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)newDeviceToken
{
ApplePushProviderService_AsyncProxy *p = [ApplePushProviderService_AsyncProxy proxyWithMessage:[ROBinMessage message]
channel:[ROHTTPClientChannel channelWithTargetUrl:URL]];
[p beginRegisterDevice:newDeviceToken:@""]; // we don't care about the response.
}
- (void)application:(UIApplication *)application didFailToRegisterForRemoteNotificationsWithError:(NSError *)error
{
NSLog(@"application:didFailToRegisterForRemoteNotificationsWithError: %@", error);
if ([error code] != 3010) // 3010 is for the iPhone Simulator
{
// show some alert or otherwise handle the failure to register.
}
}
90% of this is general plumbing for the Apple Push APIs, with your application calling registerForRemoteNotificationTypes: and getting a callback with a device token (a 32 byte binary token). Only the two lines inapplication:didRegisterForRemoteNotificationsWithDeviceToken: are specific to RemObjects.SDK.ApplePushProvider – passing the token and an optional string to the server.
The optional string parameter is provided in case your application wants to pass information to the server to influence notifications. It will be stored server side, but is not used directly by the provider code. Your own server code can look at this string to determine what notifications to send, if needed (more on that, later).
On the Server: Receiving Device Registrations
Next, let's move to the server and enable it to receive the device registrations we just implemented client-side. Simply add a reference to the RemObejcts.SDK.ApplePushProvider to your server project, and - somewhere in your server's initialization code - add the following two lines of code to let the provider know about two critical file names:
PushDeviceManager.CertificateFile = ...;
PushDeviceManager.DeviceStoreFile = ...;
The first line tells the server where to find the .p12 file that contains the Certificate you created when setting up your AppID for Push in the iPhone Developer Portal (simply export this certificate from the Keychain Access app, and copy it yo your server machine. For best ease of use, export it without a password - else, you will need to write some more custom code, server side, to load and decrypt the certificate file yourself).
The second line tells the provider where to store the file that contains information about the registered devices.
Note: the provider, as present, is implemented to store the list of registered devices in an XML file. This should work fine for most scenarios, and scale up to thousands of clients. If you are implementing a really high load server that will handle hundreds of thousands or even millions of clients, you might want to provide your own storage for device information, for example in your database. And that's it – your server is ready to receive device registrations. If you want to test this, you can add a third line below to send a message to every registered client, when the server starts up:
PushDeviceManager.PushMessageNotificationToAllDevices("Server was Started. Welcome Back!");
Of course the first time you run your server, there will be no devices yet. But if you launch your server, then your iPhone app, and then stop and restart your server, you should see the above alert message pop up on your phone.
You should also see the file specified as DeviceStoreFile above show up on your server harddisk, containing XML describing your device token and details.
Authentication
If you want to tie device notification to user accounts, or simply prevent unauthorized clients to sign up for notifications, you can set the following PushDeviceManagers' RequireSession property to true. This will force the service that handles the registration to participate in regular RemObjects SDK user authentication, as described in Login and Authentication.
This of course assumes you are providing a Login service, and have the proper delegate assigned client side, to handle authentication. All of this would of course be shared with your regular RemObjects SDK services that use authentication, so this is not really extra infrastructure specific to Push.
If RequireSession is set to true, the Push Provider will automatically store the User variable from the session, along with the device token. This will later allow you to associate the different device IDs with individual users (more on that in the next section).
Still on the Server: Sending Notifications
There's one part left to do: sending actual notifications to the registered iPhones. You-ve already seen a simple call that sent an alert to all registered devices – but in real live, you'll want more control. PushDeviceManager exposes a set of four methods that allow you to easily send a message to every device – either an alert, a sound, a badge count or a combination of the three.
In addition, it exposes a property called APSConnect (of class APSConnect), that exposes the full API for talking to the APS server, including versions of the above four methods that allow you to send notifications to a specific device, as well as to send a raw JSON data package (if you want more control over the message than the above four pairs of methods give you).
Of course, to send a message to a specific device, you need to know about devices that are registered. For this, PushDeviceManager exposes the Devices property, a dictionary that matches each device token to the information the server has stored about this device. For each device, there are three values available (in addition to the token):
- The UserReference (if you enabled authentication, as described above). You can use this value to map a particular device back to a user account, for example to send a badge reflecting that user's unread messages, or whatever other information you want to convey to the user
- ClientInfo: this is the string you passed from the client when registering your device. If you want to expose options on the phone for what kind of notification the user wants to receive, your client app can use this to send an application-specific string that you can evaluate when sending notifications. This is particularly useful if your users can choose per-device options that might be unrelated to user accounts (for example to show more/different notifications on their iPhone than on their iPod touch).
- ServerInfo: this is a string that your server can read and write to keep track of information for the device. For example, you might want to keep track of what was the last notification you sent to the device, so you won't send it again (frequently resending identical notifications, such as an unchanged badge count, can drain the user's battery).
For example, the following code might update all your devices with a badge count:
for each d in PushDevicemanager.Devices.Values do begin
var lCount: Int32 := GetUnreadMessagesForUser(d.UserReference); { your method! }
if lCount.ToString <> d.ServerInfo then begin
fAPSConnect.PushBadgeNotification(d.Token.ToArray, lCount);
d.ServerInfo := lCount.ToString;
end;
PushDeviceManager.Flush; { make sure all state gets saved to disk. }
end;
It's that easy!
Where to Get It
You can get the latest version of the ApplePushprovider code from our open source code repository at code.remobjects.com. You can access this via SVN to get the very latest, or get a pre-compiled download.
You will need Delphi Prism or the free Delphi Prism command line compiler to build the provider for source, but you can consume it from any .NET language supported by RemObejcts SDK.