Apple Push Notifications on Rails
The other night I submitted a new iPhone application to the Apple Store. The app, which I’ll speak about when, and if it gets approved, uses the new Apple Push Notification service available in iPhone OS 3.0. On the server side I have a Rails application that I am using to send the notifications to Apple. The problem I ran into was how.
Enter the APN on Rails gem. While searching I found one plugin for Rails that mostly worked for me, Sam Soffes’ apple_push_notification plugin. It was a great place to start, but I found that there were things that didn’t suite me. For starters, not having any tests is always a big turn off for me when it comes to any code. I also didn’t like that you didn’t need to save a notification in order to send it. That means you don’t have a record of what was sent and when. I also wanted to have devices stored separately from the notification. Finally, I wanted to be able to easily configure the plugin. Sam’s was using constants that would need to be changed when it hit production.
So, with all that said and done I took Sam’s great work, ripped it apart, and put it back together again, this time in gem form instead of a plugin, and here it is.
There are a few migrations, a few models, and a few Rake tasks, but here is the basic idea of how it works:
To get a better understanding of exactly how it works, and what it does, I highly recommend reading the RDOC.
There are a few things I still would like to add, for example, a controller to do CRUD for devices so iPhones can register with the Rails app. I’d also like to add a task that talks to Apple and finds out which devices are no longer accepting messages so they can be removed.
If you’d like to contribute, please feel free and for the project on GitHub:
http://github.com/markbates/apn_on_rails/tree
Again, a special thanks to Fabien Penso and Sam Soffes for their initial work on this project.
Tags: apn, apple, gem, iphone, push notification, rails, ruby

July 25th, 2009 at 2:59 pm
Very cool Mark, thanks for releasing this!
I have been using [[UIDevice currentDevice] uniqueIdentifier] to register unique IDs with my Rails app. Is the device token sufficient to replace my current unique string?
I agree that adding a RESTful controller to the gem would be a nice addition. How are you aggregating device tokens currently?
July 25th, 2009 at 3:43 pm
Hey Jerod, glad you like it. Here is a trimmed down (ie, I took out app specific stuff) version of what I have in my main app delegate:
http://gist.github.com/154957
That’s how Apple recommends you set your app up for push notifications. It will automatically give the users the pop-up asking them about APN and install it in the systems settings.
As you can see the call back gives you a NSData version of the device token, which is what you need to send to Apple with the notification message you want to push.
I have a Device object that uses ObjectiveResource to talk to my Rails app and register the device token.
Does that help?
July 26th, 2009 at 8:43 am
Mark-
Yes, that helps. Thanks.
Once installed and migrated, I assume we can safey modify the devices table without causing any problems. For instance, I’d like to associate each device to a User, which would require an extra field. No problems there?
As far as adding support for removing devices based on Apple feedback, maybe take a look at this project on GitHub which seems to have done most of the work already (again, sans tests).
http://github.com/jdg/Feedback/tree/master
July 26th, 2009 at 8:53 am
Thanks for the heads up on the feedback project Jerod, it definitely looks like it is a good start to get me where I need to be. I’ll work on getting it incorporated.
You should be able to add to a user_id column onto the devices table. I might make modifications to the table down the line, but user_id won’t be a column I add. Worse case scenario you might have to modify a future migration to avoid conflicts, but that’s pretty darn easy.
July 27th, 2009 at 11:28 am
Am I missing something or will this make an APN connection for any call to notifications? If that is so you may want to warn people not to call it for every message. I see there is a rake task but even that could cause issues if people try to call it every minute.
July 27th, 2009 at 12:17 pm
Hey Carson, not to worry it doesn’t make a call to Apple for every message. When you call APN::Notifications.send_notifications it finds all the unsent messages in the db. If there are messages that need to be sent, then it opens up one connection to Apple and pushes all the notifications through that. If there aren’t any messages, then it does nothing. Of course, it is possible to call that method after you create a new notification, but there’s not much I can do about that. Apple will shut those people down pretty quickly for constantly opening/closing connections.
Does that answer your question?
July 27th, 2009 at 12:22 pm
Carson, I also just updated the README to be a bit more explicit about this:
You can use the following Rake task to deliver your notifications:
The Rake task will find any unsent notifications in the database. If there aren’t any notifications
it will simply do nothing. If there are notifications waiting to be delivered it will open a single connection
to Apple and push all the notifications through that one connection. Apple does not like people opening/closing
connections constantly, so it’s pretty important that you are careful about batching up your notifications so
Apple doesn’t shut you down.
July 27th, 2009 at 12:50 pm
Mark, yep that change to the README is what I was thinking. People should be somewhat careful sending the notifications as it seems there is some threshold that gets you shut down.
I’ve actually been thinking it would be nice if there was a module for something like RabbitMQ that would just keep a persistent connection open and let the user do the notifications using normal message queues and get removes via subscriptions.
July 27th, 2009 at 2:28 pm
Carson I would love to see a project like that! The question is will Apple let you keep a connection open constantly like that? I suppose if it gets dropped you could detect it and re-open it. I still fear that they might not be too thrilled about an ‘always-on’ connection.
August 1st, 2009 at 5:25 pm
Apple asks for it, they said they had no problem with you keeping connected during WWDC. That’s what’s my (private) version of my Rails plugin is doing, using backgroundrb. I just had no time to release it to the public as I want to first run my service, then I’ll clean a bit of the code and release it on github.
August 1st, 2009 at 6:28 pm
Fabien, that’s good to know. I know they like it when you bulk deliver, but I didn’t think they would be good about having a persistent connection.
With the latest version, 0.3.0, I’ve wrapped the connection stuff, so you could pretty easily have it run forever with something like this:
I might put that in that version, a binary that you can run in the background that opens the connection and keeps polling for new notifications and delivers them.
August 27th, 2009 at 2:42 am
Hi Mark, First up thanks in advance for this wonderful gem, its fantastic how easy it is to get up and running.
I’m wondering if you have any benchmarks for large scale notifications (say 10000 devices on 5 min intervals). I’m wondering about the consequences of having a join to get to the device token when you could store the token as a 32 byte blob right on the notification. Some quick console hacking.
>> token_in = “ffffffffdbc0ce239b3d15e96efe4525941ebb8d8b65bc241eceffffffffffff” #Masked for security
=> “ffffffffdbc0ce239b3d15e96efe4525941ebb8d8b65bc241eceffffffffffff”
>> token_in_int = “0x#{token_in}”.to_i(16)
=> 111987304994859507620189752773925056744031439616140324133437380330181909811521
>> token_in_int.size
=> 32
>> token_in.size
=> 64
>> token_out = token_in_int.to_s(16)
=> “ffffffffdbc0ce239b3d15e96efe4525941ebb8d8b65bc241eceffffffffffff”
>> token_out.scan(/……../).join(” “) == token_in.scan(/……../).join(” “)
=> true
I’m not sure how easy it is (or isn’t) to attach a byte array to an AR::Base but for an app we’re building we’re dealing with a lot of notifications to a lot of devices so I’m wondering if getting the token down to a 32 byte blob would make it easy to just tack it onto the notification and save a join in the query.
Just a thought,
Corey
August 27th, 2009 at 9:41 am
Hi Corey, I’m glad you like the gem. I aim to please.
I can’t say I have those types of benchmarks, as I’ve been working on a much smaller scale in terms of notifications. I designed the plugin to use normalized data, so I didn’t have to keep storing redundant info in the notifications table. You could easily pull it all back in one query, if you think that’s a bottleneck. What I can say is that pulling 10k records in 5 minutes with AR is totally doable, I don’t think that’s where any bottlenecks would occur. If you get some benchmarks I would love to see them.
As for storing byte arrays to AR, I’m sure it’s possible, again it’s not something I’ve had to do before with AR. If you want to fork the project on GitHub and can come up with some great performance enhancements that make sense, I’ll gladly pull that back into the project. I love when others contribute and make the project better.
September 7th, 2009 at 1:32 am
Looks cool. Thanks for the kind words. Yours looks pretty cool.
September 17th, 2009 at 4:34 am
Do you know if your plugin will allow for notifications to be sent for unlocked phones? I heard that it doesn’t always work with unlocked phones…any details on this subject? Thanks.