pushito v0.1.0 Pushito View Source

Pushito is an APNs library over the HTTP/2 API. This is the Main module and the one you should use in your applications.

Connect to APNs

In order to create connections we will use Pushito.connect/1 function. It only has one argument which is the config information. The config is a Pushito.Config struct and its format is:

defstruct type: :cert,
          name: nil,
          apple_host: nil,
          cert_file: nil,
          key_file: nil,
          token_key_file: nil,
          token_key_id: nil,
          team_id: nil

Pushito.Config provides setters in order to set the properties.

There are two types of connection with APNs, cert and token. The first one is with Provider Certificate and the second one is with Provider Authentication Tokens. Depending the type you should fill some properties in the config structure.

  • For Provider Certificate type Apple gives us a cer file. We need to convert it to 2 pem files, you can check here how to do it
  • For Provider Authentication Token Apple gives us a .p8 file. It is fine, no conversion is needed.

Mandatory Properties for both types

  • type: can be :cert or :token, default value is :cert
  • name: this is the name of the connection, it must be an :atom
  • apple_host: the Apple host. Currently only api.development.push.apple.com or api.push.apple.com are available.
import Pushito.Config

config = new()
         |> add_name(:my_first_connection)
         |> add_type(:cert)
         |> add_host("api.development.push.apple.com")

Mandatory Properties for Provider Certificate type

  • cert_file: path to your certification file.
  • key_file: path to key file
config
|> add_cert_file("priv/cert2.pem")
|> add_key_file("priv/key2-noenc.pem")

Mandatory Properties for Provider Authentication Tokens type

  • token_key_file is the path to the token key file provided by Apple
  • token_key_id is the key id provided by Apple
  • team_id is your team developer id
config
|> add_token_key_file("priv/APNsAuthKey_1234567890.p8")
|> add_token_key_id("1234567890")
|> add_team_id("THEATEAM")

Connect

Once we have the config structure we can connect to APNs using Pushito.connect/1

{:ok, connection_pid} = Pushito.connect(config)

connection_pid is the connection pid. It is only needed if we want to supervise it directly.

Push Notifications

Important The process which calls Pushito.connect/1 should be the same as the one which calls Pushito.push/2

Once we have the connection done we can start pushing messages to APNs. In order to do that we will use Pushito.push/2. The first argument is the connection_name and the second one is the notification itself. The notification is a Pushito.Notification structure with the format:

defstruct device_id: nil,
          apns_expiration: 0,
          apns_priority: 10,
          apns_topic: nil,
          apns_id: nil,
          apns_collapse_id: nil,
          token: nil,
          timeout: 10,
          message: %{}

Depending the connection type there are some mandatory properties to fill.

Mandatory Properties for both types

  • device_id: the device id we want to send the message
  • apns_topic: the topic identifying your App
  • message: the message we want to send
import Pushito.Notification

notification = new()
               |> add_device_id("bd5c3ad01bbe4d884bf2fe8801ed77e94a71bc2e9de937c84f745f54eb4cb2f4")
               |> add_topic("com.inaka.myapp")
               |> add_message(%{:aps => %{:alert => "you have a message!!"}})

Mandatory Properties for Provider Authentication Tokens type

  • token: The JWT token needed for push notifications with tokens. In order to get a token we have to call Pushito.generate_token/1 where the argument is the connection_name
token = Pushito.generate_token(:my_token_connection)

notification
|> add_token(token)

Optional Properties for both types

  • timeout: the seconds util throw a timeout. If you get a timeout it means you exceed the time but maybe your notification was sent correctly. Default value is 10 seconds

Those are the remaining apns fields. You can check them here

  • apns_expiration: default value is 0
  • apns_priority: default value is 10
  • apns_id: can be ignored. APNs will assign one and add it to the response
  • apns_collapse_id
notification
|> add_timeout(20)
|> add_expiration(0)
|> add_priority(10)
|> add_id("ID")
|> add_collapse_id("collapse_id")

Response

If you don’t get a timeout after pushing a message you must receive a response. Every request to APNs servers returns a Response.

The Responses are Pushito.Response struct with the format:

defstruct status: nil, headers: [], body: :no_body
  • status: is the http2 result code. You can check the HTTP/2 Response from APN section
  • headers: is a list with the response headers
  • body: is a list with the body or :no_body when no body is returned

Timeout

If the call to Pushito.push/2 returns a {:timeout, stream_id} it means your process exceeded the timeout time waiting for a response from APNs. That could be caused because your timeout should be greater, because the network went down or maybe other causes.

If you get a timeout it doesn’t mean your notification wasn’t delivered correctly for sure. If the network went down chatterbox (the HTTP/2 client pushito relies) will try to connect again and it will send the message when the network goes up. If that is the case the caller process will receive a message on its mail box with the format {:apns_response, connection_pid, stream_id, response} where the connection_pid is the connection pid (same as Process.whereis(connection_name)) and the stream_id is the notification identifier returned in the timeout tuple.

Reconnections

If something unexpected happens and the chatterbox connection with APNs crashes pushito will send a message {:reconnecting, connection_pid} to the client process, that means pushito lost the connection and it is trying to reconnect. Once the connection has been recover a {:connection_up, connection_pid} message will be send.

We implemented an Exponential Backoff strategy.

Closing Connections

Apple recommends us to keep our connections open and avoid opening and closing very often. You can check the Best Practices for Managing Connections.

But when closing a connection makes sense pushito gives us the function Pushito.close/1 where the parameter is the connection’s name. After closing a connection the name will be available for new connections again.

Link to this section Summary

Functions

Closes a connection by connection name

Creates a connection with APNs

Generates a JWT token in order to push notifications

Push notification to APNs with Provider Certificate

Application callback for starting Pushito

Link to this section Types

Link to this type connection_name() View Source
connection_name() :: atom

Link to this section Functions

Link to this function close(connection_name) View Source
close(connection_name) :: :ok

Closes a connection by connection name

Example

It will close the given connection name

Pushito.close :my_connection
:ok
Link to this function connect(config) View Source
connect(Pushito.Config.t) :: {:ok, pid}

Creates a connection with APNs

Example

We need a valid Pushito.Config structure, you can see above how to create it:

{:ok, connection_pid} = Pushito.connect config
{:ok, #PID<0.233.0>}
Link to this function generate_token(connection_name) View Source
generate_token(connection_name) :: String.t

Generates a JWT token in order to push notifications.

This only makes sense when the connection type is token. The lifetime of each token will be 1 hour then it will expire

Example

In order to create the token, we must be added the mandatory fields for token type connections:

config
|> add_token_key_file("priv/APNsAuthKey_1234567890.p8")
|> add_token_key_id("1234567890")
|> add_team_id("THEATEAM")

Then we can create the token with the connection_name as an argument:

token = Pushito.generate_token :my_connection
"eyJhbGciOiJFUzI1NiIsImtpZCI6IjEyMzQ1Njc4OTAiLCJ0eXAiOiJKV1QifQ.eyJleHAiOjE0OTY5NDI1NzgsImlhdCI6MTQ5NjkzNTM3OCwiaXNzIjoiVEhFQVRFQU0iLCJuYmYiOjE0OTY5MzUzNzd9.5dCjXP-JTsJaGND9MqBEnWkzBb2-Wya1wv9I0p8ljQTtdybl6Vnc3H5St88HEFMLOrFzUMhrbMy04Pg42sshMQ"
Link to this function push(connection_name, notification) View Source

Push notification to APNs with Provider Certificate

Example

We need a valid Pushito.Notification struct before push something, you can see above how to create it.

Pushito.push :my_connection, notification
%Pushito.Response{body: :no_body,
 headers: [{"apns-id", "34F4B4F4-ADB6-982F-EB23-36632837520C"}], status: 200}

That was a succesfull request but it can return errors or timeout, lets force a timeout:

notification = notification |> Pushito.Notification.add_timeout(0)
:ok

Pushito.push :my_connection, notification
{:timeout, 3}

But it only means the time exceeded the timeout, we can see in the process mailbox if it worked:

flush()
{:apns_response, #PID<0.250.0>, 3,
 %Pushito.Response{body: :no_body,
  headers: [{"apns-id", "AA2C383F-2222-DC0B-B4B8-BA2E8A4F46F4"}], status: 200}}
:ok

Application callback for starting Pushito.