Build StatusHex.pmHex.pm

Pigeon

HTTP2-compliant wrapper for sending iOS and Android push notifications.

Installation

Add pigeon as a mix.exs dependency:

Note: Pigeon's API will likely change until v1.0

def deps do
[{:pigeon, "~> 0.5.0"}]
end

After running mix deps.get, configure mix.exs to start the application automatically.

def application do
[applications: [:pigeon]]
end

GCM (Android)

Usage

  1. Set your environment variables.
config :pigeon,
gcm_key: "your_gcm_key_here"
  1. Create a notification packet.
data = %{ message: "your message" }
n = Pigeon.GCM.Notification.new(data, "your device registration ID")
  1. Send the packet.
Pigeon.GCM.push(n)

Sending to Multiple Registration IDs

Pass in a list of registration IDs, as many as you want. IDs will automatically be chunked into sets of 1000 before sending the push (as per GCM guidelines).

data = %{ message: "your message" }
n = Pigeon.GCM.Notification.new(data, ["first ID", "second ID"])

Notification Struct

When using Pigeon.GCM.Notification.new/2, message_id and updated_registration will always be nil. These keys are set in the response callback. registration_id can either be a single string or a list of strings.

%Pigeon.GCM.Notification{
data: nil,
message_id: nil,
registration_id: nil,
updated_registration_id: nil
}

APNS (Apple iOS)

Usage

  1. Set your environment variables. See below for setting up your certificate and key.
config :pigeon,
apns_mode: :dev,
apns_cert: "cert.pem",
apns_key: "key_unencrypted.pem"
  1. Create a notification packet. Note: Your push topic is generally the app's bundle identifier.
n = Pigeon.APNS.Notification.new("your message", "your device token", "your push topic")
  1. Send the packet.
Pigeon.APNS.push(n)

Generating Your Certificate and Key .pem

  1. In Keychain Access, right-click your push certificate and select "Export..."

  2. Export the certificate as cert.p12

  3. Click the dropdown arrow next to the certificate, right-click the private key and select "Export..."

  4. Export the private key as key.p12

  5. From a shell, convert the certificate.

    openssl pkcs12 -clcerts -nokeys -out cert.pem -in cert.p12
  6. Convert the key.

    openssl pkcs12 -nocerts -out key.pem -in key.p12
  7. Remove the password from the key.

    openssl rsa -in key.pem -out key_unencrypted.pem
  8. cert.pem and key_unencrypted.pem can now be used as the cert and key in Pigeon.push, respectively. Set them in your config.exs

Notifications with Custom Data

Notifications can contain additional information for the aps key with a map passed as an optional 4th parameter (e.g. setting badge counters or defining custom sounds)

n = Pigeon.APNS.Notification.new("your message", "your device token", "your push topic", %{
badge: 5,
sound: "default"
})

Or define custom payload data with an optional 5th parameter:

n = Pigeon.APNS.Notification.new("your message", "your device token", "your push topic", %{}, %{
your-custom-key: %{
custom-value: 500
}
})

Handling Push Responses

GCM

  1. Pass an optional anonymous function as your second parameter. This function will get called on each registration ID assuming some of them were successful.
data = %{ message: "your message" }
n = Pigeon.GCM.Notification.new(data, "device registration ID")
Pigeon.GCM.push(n, fn(x) -> IO.inspect(x) end)
  1. Reponses return a tuple of either {:ok, notification} or {:error, reason, notification}. You could handle responses like so:
on_response = fn(x) ->
case x do
{:ok, notification} ->
# Push successful, check to see if the registration ID changed
if !is_nil(notification.updated_registration_id) do
# Update the registration ID in the database
end
{:error, :InvalidRegistration, notification} ->
# Remove the bad ID from the database
{:error, reason, notification} ->
# Handle other errors
end
end
data = %{ message: "your message" }
n = Pigeon.GCM.Notification.new(data, "your device token")
Pigeon.GCM.push(n, on_response)

Notification structs returned as {:ok, notification} will always contain exactly one registration ID for the registration_id key.

For {:error, reason, notification} tuples, this key can be one or many IDs depending on the error. :InvalidRegistration will return exactly one, whereas :AuthenticationError and :InternalServerError will return up to 1000 IDs (and the callback called for each failed 1000-chunked request).

Error Responses

Slightly modified from GCM Server Reference

ReasonDescription
:MissingRegistrationMissing Registration Token
:InvalidRegistrationInvalid Registration Token
:NotRegisteredUnregistered Device
:InvalidPackageNameInvalid Package Name
:AuthenticationErrorAuthentication Error
:MismatchSenderIdMismatched Sender
:InvalidJSONInvalid JSON
:MessageTooBigMessage Too Big
:InvalidDataKeyInvalid Data Key
:InvalidTtlInvalid Time to Live
:UnavailableTimeout
:InternalServerErrorInternal Server Error
:DeviceMessageRateExceededMessage Rate Exceeded
:TopicsMessageRateExceededTopics Message Rate Exceeded
:UnknownErrorUnknown Error

APNS

  1. Pass an optional anonymous function as your second parameter.
n = Pigeon.APNS.Notification.new("your message", "your device token", "your push topic")
Pigeon.APNS.push(n, fn(x) -> IO.inspect(x) end)
  1. Responses return a tuple of either {:ok, notification} or {:error, reason, notification}. You could handle responses like so:
on_response = fn(x) ->
case x do
{:ok, notification} ->
Logger.debug "Push successful!"
{:error, :BadDeviceToken, notification} ->
Logger.error "Bad device token!"
{:error, reason, notification} ->
Logger.error "Some other error happened."
end
end
n = Pigeon.APNS.Notification.new("your message", "your device token", "your push topic")
Pigeon.APNS.push(n, on_response)

Error Responses

Taken from APNS Provider API

ReasonDescription
:PayloadEmptyThe message payload was empty.
:PayloadTooLargeThe message payload was too large. The maximum payload size is 4096 bytes.
:BadTopicThe apns-topic was invalid.
:TopicDisallowedPushing to this topic is not allowed.
:BadMessageIdThe apns-id value is bad.
:BadExpirationDateThe apns-expiration value is bad.
:BadPriorityThe apns-priority value is bad.
:MissingDeviceTokenThe device token is not specified in the request :path. Verify that the :path header contains the device token.
:BadDeviceTokenThe specified device token was bad. Verify that the request contains a valid token and that the token matches the environment.
:DeviceTokenNotForTopicThe device token does not match the specified topic.
:UnregisteredThe device token is inactive for the specified topic.
:DuplicateHeadersOne or more headers were repeated.
:BadCertificateEnvironmentThe client certificate was for the wrong environment.
:BadCertificateThe certificate was bad.
:ForbiddenThe specified action is not allowed.
:BadPathThe request contained a bad :path value.
:MethodNotAllowedThe specified :method was not POST.
:TooManyRequestsToo many requests were made consecutively to the same device token.
:IdleTimeoutIdle time out.
:ShutdownThe server is shutting down.
:InternalServerErrorAn internal server error occurred.
:ServiceUnavailableThe service is unavailable.
:MissingTopicThe apns-topic header of the request was not specified and was required. The apns-topic header is mandatory when the client is connected using a certificate that supports multiple topics.