Project S - Day 3: Coding the back-end to send Push Notifications to our App!
In my previous blog, Project S - Day 2, I talked about iOS development and Push Notifications.
Today, I'm going to talk about connecting our primitive back-end to the APNs (Apple Push Notification services) to send some test notifications to our App-in-the-making.
APNs
If you read my previous blog, you might remember the diagram depicting delivery of a remote notification from a provider to an app. If not, here it is again for completeness' sake:
©Apple
Communication between the app/device and the APNs is automatically handled by Apple's servers. The only thing we need to do, is to register the app with the APNs. Once that is done, the APNs will send back a unique device token to the app. We need this token on the server side (Provider), to be able to send a notification to the device. You can think of it as an unique address for that device.
Connecting
On the Provider side, things become a little more complicated. First we need to connect to the APNs somehow. This can be done with a so called HTTP/2 POST-request. It's similar to how your browser sends a login-form to the website's server you want to login to. Authentication is done with a certificate, which proves to the APNs that the Provider is allowed to connect. When the connection is successful the Provider sends the actual notification message to the APNs, also including the device token so it knows to what device it should forward it.
NOTE:
There is also the option of authenticating with an app token in stead of a certificate. This token is then used to sign an authentication message.
Sending Push Notifications
OK, so our goal today is to send push notifications from our Python code. Let's see how far we get.
First off, we need the push notifications certificate from our keychain. I exported it to a .p12 file (binary) and then used openssl
to convert it to a plain-text version (.pem) which includes the public as well as the private key. It is important to not encrypt it with a password. Otherwise it is impossible for the Python program to read it. The command I used was as follows: openssl pkcs12 -in ./iOS-dev.p12 -out ./ios-dev-client-cert.pem -clcerts -nodes
.
Next step is to install the python-hyper module to be able to make HTTP/2 requests: pip install hyper
Great! We're all set for a test:
>>> import json
>>> import hyper
>>> from hyper import HTTP20Connection
>>> context = hyper.tls.init_context(cert='/Users/ben/Desktop/ios-dev-client-cert.pem')
<ssl.SSLContext object at 0x100f3ab38>
>>> host = 'api.development.push.apple.com'
>>> port = 443
>>> conn = HTTP20Connection(host, port=port, ssl_context=context)
>>> conn
<hyper.http20.connection.HTTP20Connection object at 0x100f679b0>
>>> url = '/3/device/40a5b1b1d5xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxdba'
>>> headers = {'apns-expiration': "0", 'apns-priority': "10"}
>>> payload = json.dumps({'aps': { 'alert': "Yo, rukker!" }})
>>> payload
'{"aps": {"alert": "Yo, rukker!"}}'
>>> conn.request('POST', url, body=payload, headers=headers)
5
>>> conn.get_response()
<hyper.http20.response.HTTP20Response object at 0x10152cb38>
>>> _.status
200
>>>
The context
object is important here. We tell it to use our personal certificate obtained from Apple. This way, the HTTP20Connection
object can authenticate with the APNs. The url
contains our unique device token I talked about.
Result!
NOTE:
If you start programming in Python, it is preferable to create a virtual environment. In this way, you can install 3rd party modules and dependencies without breaking other software. Each project gets its own Python environment, with its own modules. See also the Virtualenv documentation for more in-depth info.
NOTE2:
I'm using Python 3.6 for this project. I would advise to use at least Python 3.4 if you start new projects, unless you really have a good reason not to. Installing different Python versions, differs from system to system, so I would recommend you check out the original docs for instructions.
That was it for today. Tomorrow I'll talk a bit about some design considerations.
@bennierex This is actually some incredible function!.
Great write-up man, some of it is over my head. But you are a pro!
Don't worry, we'll make a success together.