Changing the language setting in HKWarnings

A few people are confused as to how to change the language in HKWarnings.

The interface language is determined by your iPhone language setting. So if your phone is set to English, you get this:

If you are set to Traditional Chinese you get this:

Notice the warning language is still in English. That’s controlled by a separate setting.

The language the warnings are displayed in is set in the iPhone Settings app. This is based on the design guideline from Apple:

Adding your application preferences to the Settings application is most appropriate for productivity-style applications and in situations where you have preference values that are typically configured once and then rarely changed.

The language of choice will rarely be changed, so it’s in the Settings app.

Scroll down to the HKWarnings entry:

And there you can change the warning language:

or in Chinese:

If you switch to Chinese warnings on an English phone you will get:

Or Chinese on a Chinese phone:

HKWarnings v1.2 Approved

HKWarnings v1.2 is ready for download.

What’s new:

  • Push notifications. Now when the HK Observatory issues a weather warning a notification is sent to your iPhone. You’ll know straight away if there is a T8 and it’s time to go home!
  • iOS 4 compatible.

HKWarnings v1.2 Submitted For Review

HKWarnings v1.2 has been submitted to Apple for review.

What’s new?

  • Push notifications. Now when the HK Observatory issues a weather warning a notification is sent to your iPhone. You’ll know straight away if there is a T8 and it’s time to go home.
  • iOS 4 compatible.

Using PHP to send notifications to Urban Airship

Previously I wrote about how to use Urban Airship for push notifications. I covered how to setup Urban Airship and your iPhone app code, however, you still need to write a server that sends the notifications to UA.

I decided to use PHP and develop locally on my Mac at first:

Get MAMP – I chose to get MAMP Pro too but you don’t need it.
Get the Urban Airship PHP lib (There are other libraries here).

You need to setup pear as the UA library depends on HTTP_Request:

/usr/lib/php/pear config-create $HOME .pearrc
/usr/lib/php/ install -o PEAR
/usr/lib/php//pear install install Net_URL
/usr/lib/php//pear install HTTP_Request

For MAMP you will need to add this to your script:

ini_set( 'include_path', ini_get( 'include_path' ) . PATH_SEPARATOR . "/usr/lib/php");

That’s it! Put your app key/master secret in the sample code and test.

Once it’s looking good you could continue to run on your Mac, but I decided to put on my web hosting server.

I’m using DreamHost, so with help from here, perform the same pear installs:

/usr/local/php5/bin/pear config-create $HOME .pearrc
/usr/local/php5/bin/pear install -o PEAR
/usr/local/php5/bin/pear install install Net_URL
/usr/local/php5/bin/pear install HTTP_Request

Then to your script, add:

ini_set( 'include_path', ini_get( 'include_path' ) . PATH_SEPARATOR . "/home/.[machinename]/[username]/pear/php");

Later you might want to run from the command line via cron – make sure you use PHP5:

/usr/local/php5/bin/php -f myPush Server.php >> log.log 

Push Notifications with Urban Airship

The simplest way to set up your app for push notifications, I think, is to use Urban Airship? and their push product:

Urban Airship Push is a scalable, easy-to-implement solution that allows you to take advantage of the rich possibilities offered by push notifications?.

They say:

We can get you up and running within a few short hours?

It’s quicker than that.

Here are the steps I followed.

  • Join Urban Airship (UA)
  • Provision your app for push in the developer portal, generate a new certificate signing request and download your new certificate.
  • Create your app in UA and upload the certificate. UA have some instructions here.
  • You might need to regenerate your provisioning profile and reinstall – just to be safe.
  • Add the code to your app to register and handle push notifications. UA have some sample code.
  • Choose the right provisioning profile? and build.

That’s it! When the app starts up it registers with Apple for push notifications, if that’s successful it registers with UA. You are then ready to send notifications. You can test from the UA website.

You will need to write a service to push notifications to UA. I’ll cover that in another post.

Some gotchas and notes

Make sure you choose the right provisioning profile when building otherwise you will see the error below when you run:

no valid 'aps-environment' entitlement string found for application

Make sure you use a dev provisioning profile with the dev app you set up in UA or the prod/adhoc profile with the production app. Otherwise the Apple Push service will reject the device token and UA marks the token as inactive.

If you only ever need to broadcast to all devices, you do not need to keep a list of valid devices.

If you want to be able to send to individual devices or groups (using the UA tags) then you need to keep a list of valid devices and keep the list up to date by querying the UA feedback service.

The code you need to add to your app:

#define kApplicationKey @"YOUR APP KEY"
#define kApplicationSecret @"YOUR APP SECRET"

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {

// reset notification badge count
application.applicationIconBadgeNumber = 0;

//Register for notifications
[[UIApplication sharedApplication]
registerForRemoteNotificationTypes:(UIRemoteNotificationTypeBadge |
UIRemoteNotificationTypeSound |

- (void)application:(UIApplication *)application didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)_deviceToken {

// Get a hex string from the device token with no spaces or < >
self.deviceToken = [[[[_deviceToken description] stringByReplacingOccurrencesOfString:@"<"withString:@""]
stringByReplacingOccurrencesOfString:@">" withString:@""]
stringByReplacingOccurrencesOfString: @" " withString: @""];

NSLog(@"Device Token: %@", self.deviceToken);

if ([application enabledRemoteNotificationTypes] == 0) {
NSLog(@"Notifications are disabled for this application. Not registering with Urban Airship");

// this is straight out of the UA sample code
NSOperationQueue *queue = [[[NSOperationQueue alloc] init] autorelease];
NSString *UAServer = @"";
NSString *urlString = [NSString stringWithFormat:@"%@%@%@/", UAServer, @"/api/device_tokens/", self.deviceToken];
NSURL *url = [NSURL URLWithString: urlString];
ASIHTTPRequest *request = [[[ASIHTTPRequest alloc] initWithURL:url] autorelease];
request.requestMethod = @"PUT";

// Authenticate to the server
request.username = kApplicationKey;
request.password = kApplicationSecret;

[request setDelegate:self];
[request setDidFinishSelector: @selector(registrationSuccessMethod:)]; // if you want to do something with the token
[request setDidFailSelector: @selector(requestWentWrong:)];
[queue addOperation:request];

- (void)application:(UIApplication *)application didFailToRegisterForRemoteNotificationsWithError:(NSError *) error {
NSLog(@"Failed to register with error: %@", error);

- (void)requestWentWrong:(ASIHTTPRequest *)request {

[UIApplication sharedApplication].networkActivityIndicatorVisible = NO;
NSError *_error = [request error];

NSLog(@"ERROR: NSError query result: %@", _error);

UIAlertView *someError = [[UIAlertView alloc] initWithTitle:
@"Network error" message: NSLocalizedString( @"Error registering with notifiction server",
@"Error registering with notifiction server")
delegate: self
cancelButtonTitle: @"OK"
otherButtonTitles: nil];
[someError show];
[someError release];

If you want to store the device tokens then:

- (void)registrationSuccessMethod:(ASIHTTPRequest *) request {

[UIApplication sharedApplication].networkActivityIndicatorVisible = NO;

// send to your server
// however you like

The iPhoneOS handles push notifications when your app isn’t running, but what if you receive a push when your app is open? You need to implement:

- (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo
NSDictionary *apsInfo = [userInfo objectForKey:@"aps"];
NSString *alert = [apsInfo objectForKey:@"alert"];
NSString *sound = [apsInfo objectForKey:@"sound"];
NSString *badge = [apsInfo objectForKey:@"badge"];

// if you want to set the badge number
application.applicationIconBadgeNumber = [[apsInfo objectForKey:@"badge"] integerValue];

// more likely you want to set it to zero
application.applicationIconBadgeNumber = 0;

// play sound?

// show push msg
UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:@"Push Notification" message:alert delegate:nil cancelButtonTitle:@"OK" otherButtonTitles:nil];
[alertView show];
[alertView release];