|October 12th, 2011|
In a previous blog post I outlined my need for an iOS server. I had found a sufficient but non-optimal solution for iOS devices running iOS 4.X. I mentioned at the end of that article that I had found an optimal solution utilizing some new features in iOS 5. Now that iOS 5 has gone gold master and the NDA has been lifted I can outline in detail how to get your own iOS server up and running. To review, the three requirements for setting up a server in my situation are that it must:
- Be able to receive push notifications (so it can get it’s work)
- Have it’s display turned off (to save energy and avoid things like screen burn-in/fatigue)
- Require no human interaction (needs to be completely autonomous)
In the previous article I outlined why these were in conflict with each other on iOS 4 devices. However, there is some new functionality and behavior policies that allow all three requirements to be fulfilled.
The Solution? Newsstand
Yes that’s right, Newsstand. As in the virtual magazine functionality added to iOS 5. It seems that Newsstand apps are granted a nice little exception in iOS 5 in which they are able to receive push notifications even if the app is not the active application. Newsstand apps are the only apps that are currently granted this exception and so other apps will still not receive delivery of push notifications even if they are in the foreground but not the active application. So the first thing you need to do is add the
UINewsstandApp key with a value of
YES to your application’s Info.plist. In Xcode, my server app is a different build configuration of the client app and so I added this line to a build script:
/usr/libexec/PlistBuddy -c "Add :UINewsstandApp bool YES" \ "$CONFIGURATION_BUILD_DIR/$PRODUCT_NAME.$WRAPPER_EXTENSION/Info.plist"
Another Day, Another Mode
Now Springboard thinks your app is a Newsstand app and so it will from now on live in the magazine shelf inside of Springboard. The next thing you need to do is add
newsstand-content to your
UIBackgroundModes in the Info.plist. (in my project I don’t have any other background modes and so I just deleted the old one every time) Now this will allow your applications to receive push notifications without being active.
/usr/libexec/PlistBuddy -c "Delete :UIBackgroundModes" \ "$CONFIGURATION_BUILD_DIR/$PRODUCT_NAME.$WRAPPER_EXTENSION/Info.plist" /usr/libexec/PlistBuddy -c "Add :UIBackgroundModes array" \ "$CONFIGURATION_BUILD_DIR/$PRODUCT_NAME.$WRAPPER_EXTENSION/Info.plist" /usr/libexec/PlistBuddy -c "Add :UIBackgroundModes:0 string newsstand-content" \ "$CONFIGURATION_BUILD_DIR/$PRODUCT_NAME.$WRAPPER_EXTENSION/Info.plist"
The Special Bit
Your app will now receive the special push notifications but said push notifications need to be flagged as Newsstand content for the system to actually pass them along to your app. To do that you simply need to add one more field in your JSON push notification that you send to Apple’s servers:
$payload['aps']['content-available'] = 1;
Note that that is the number 1, NOT the string “1”.
Inside your app’s execution you of course need to register to receive push notifications so that they are actually delivered to your app. There is a new notification type that corresponds to the Newsstand content flagged notifications called
UIRemoteNotificationTypeNewsstandContentAvailability. In my app I register for all types of notifications:
[[UIApplication sharedApplication] registerForRemoteNotificationTypes:(UIRemoteNotificationType)( UIRemoteNotificationTypeBadge | UIRemoteNotificationTypeSound | UIRemoteNotificationTypeAlert | UIRemoteNotificationTypeNewsstandContentAvailability)];
One Step Forward, Two Steps Back
We have gotten pretty far but there is still a slight roadblock in achieving our goal of an iOS server. You might notice now that on iOS 5 when you hit the lock button not only does your app become inactive but it also gets backgrounded (this is in contrast to the iOS 4 behavior of becoming inactive but remaining in the foreground executing). The solution to this is to actually take a step backwards and remove it’s iOS multitasking capabilities. You can do this by adding the
UIApplicationExitsOnSuspend key to the Info.plist:
/usr/libexec/PlistBuddy -c "Set :UIApplicationExitsOnSuspend YES" \ "$CONFIGURATION_BUILD_DIR/$PRODUCT_NAME.$WRAPPER_EXTENSION/Info.plist"
What this does is it returns your app to pre-multitasking behavior so instead of continuing to execute or backgrounding when you hit the home button your server app now exits. This has an interesting side effect of keeping the app both in the foreground and executing even when the lock screen is up (to preserve behavior of applications written this way no doubt). So now your program not only keeps running while the lock screen is up and the display is off but it can also receive and immediately service push notifications.
The Unlimited Plan
But wait! We aren’t quite out of the woods yet. You might have noticed that you are only allowed one non-active delivered push notification per 24 hour period. Obviously you want your server to receive as many push notifications as you need. You can unlock this restriction actually by writing into your defaults the key
NKDontThrottleNewsstandContentNotifications when you launch:
[[NSUserDefaults standardUserDefaults] setBool:YES forKey:@"NKDontThrottleNewsstandContentNotifications"];
Now you have it! An iOS server that can completely power down it’s display, receive push notifications and require no human interaction. Now just plug in into a power source, sit it in the closet and let it do it’s thing.
Of course, you realize that an app authored in such a way would never pass the muster required to make it on to the App Store. But then again, this app is probably never going to leave your closet or server room anyway right?