Java – Sending Push Notifications to iOS Devices

iosjavapush

I have a seemingly standard task of sending push notifications to Apple devices from a server written in Java. I have never done that in the past, so I went to tutorials and Apple documentation.

1) The APN Provider API claims that THE protocol to send push notifications from the server is HTTP/2. I failed to find any tutorials describing how to do so (I wanted those mostly as a sign that I'm not the first one to do that and maybe for references to the most popular tools/libraries). I found several tutorials (Ray Wenderlich tutorial probably being the most cited) that teach how to send push notifications using the binary protocol. Most GitHub projects use it as well (e.g. a "push notification simulator kit from a Ray Wenderlich tutorial", a popular "pushmeup" ruby gem, a very popular notnoop/java-apns library and so on). Now, the Apple docs have a section for binary provider API which states that

The legacy binary interface required your provider server to employ
the binary API described in this appendix. All developers should
migrate their remote notification provider servers to the more capable
and more efficient HTTP/2-based API described in APNs Provider API.

This definitely sounds like I shouldn't be using the binary API for new development… Even more so since the binary API requires usage (periodic polling) of a separate Feedback service for "dead" device tokens (or else you might be blocked they say), while the new HTTP/2 API gives this information as a response to each individual request, which looks like less moving parts.

As for the HTTP/2, there is a Java library CleverTap/apns-http2 which is even relatively popular, but it uses Jetty HTTP/2 client, and for it to work one needs to start JVM with an ALPN jar on the boot classpath, and that jar corresponds approximately 1-to-1 with JDK versions (which means that if your development uses 1.8.0u92 and your production uses 1.8.0u91 then there should be different jars put). That looks like a very brittle configuration to me in general and hard to apply in my case (each developer has his own VM + common dev VM + test server + prod) — I don't think it's worth the effort to freeze one version on all of the machines and that these won't diverge with time; all I know is that all of them use JDK 8, but minor versions are out of my control. This need for the ALPN jar will be the case until JDK 9 is out (and adopted widely) which means 1+ year, and it is the case for Jetty and Netty and okhttp3 and I guess any Java library.

2) As if this was not enough, there are also two authentication schemes described by the Apple docs: using a certificate ("old style") vs using JWT. The certificate approach is tried-and-true, but from the docs it looks like the certificate should be regenerated every year while JWTs don't (or to be more precise JWTs are regenerated every hour automatically and so that will be baked into the application and hopefully won't require any manual administrative actions that can be forgotten etc.). The only problem with JWT for me is that

After you create the token, you must sign it with a private key as
described in Creating a Universal Provider Token Signing Key in App Distribution Guide.

The problem is that there is no such section or title or anything in that guide. Perhaps this step is very simple, but not having any hands-on experience with iOS security in particular and little experience with cryptography in general I'm afraid I'll miss something here. I can ask our iOS developers for help but I'm not even sure this approach is mature enough if I can find zero working tutorials.

So, my questions finally are (pick any):

  • Am I missing something and there is a way to follow Apple's guidelines and have the solution be reasonably simple?
  • How likely is it that the binary protocol will be terminated "soon"?
  • Should I choose the binary protocol or HTTP/2?
  • If it's the latter, should I pursue using JWT or is using a certificate more practical?
  • Maybe the situation (mostly with HTTP/2) is much better in some other languages/server platforms? I might even think about having a separate service just for sending iOS pushes done in that if that means jumping through less hoops.

Thank you, sorry for the lengthy text.

Best Answer

We have implemented several push servers and all of them following the old tuple "socket-certificate".In Java (1.5 - 1.7).

To work with certificates has some disadvantages. For instance, we need one for each environment (test, pro, etc). You have to be methodic at managing them or it's quite easy to end up with the wrong cert in pro. Or to forget its renewal (they also expire).

Related to the socket, this approach requires having opened a specific range of ports in the firewall.

Related to the whole protocol of communication. You get so little info after pushing messages. It's hard to figure out what happened with the messages. The only way is to retrieve messages from the queue of responses. A queue which orders is not guaranteed. Neither when APNS is going to put the responses onto it. It might not happen at all.

Compared to GCM (Google cloud message which runs via HTTP), the "socket-cert" of APNS is a pain in the ...

My suggestion is to get focus on the HTTP 2 - JWT protocol. This is a very common implementation of security in client-server communications. You will find many more references about http2 and JWT than looking for APNS sockets and Certs.

Security via JWT is commonly implemented these days. There is full support from the community.

Moreover, If they have planned to drop the support to the current implementation, why even to dare to try it? Why spend time and money twice?

Be preventive. Implementing HTTP2 - JWT approach will save you from further code reviews and refactors. In any case, it's a work to do, so better have it done sooner than later.

Related to the library CleverTap. Well nobody is stopping you from implementing your own client! Suited to your need and requirements.

This has been our case with our current engine. We discarded all the 3rd party implementations and built our own. So far I know it keeps working perfectly... Till Apple drops the service.

(If we didn't move yet to HTTP2 - JWT is due to time and money)

There's perhaps alternatives. Google Firebase Cloud Message is multi-platform. So you can push messages to Android and iOs devices from the same service. It works over HTTP and API keys (tokens). I suggest to you to take a look.

Related Topic