HTTP2-compliant wrapper for sending iOS and Android push notifications.
Add pigeon and kadabra as mix.exs
dependencies:
def deps do
[
{:pigeon, "~> 1.6.0"},
{:kadabra, "~> 0.4.4"}
]
end
-
Add a default worker config to your mix config. See the detailed docs for setting up your certificate and key.
config :pigeon, :apns, apns_default: %{ cert: "cert.pem", key: "key_unencrypted.pem", mode: :dev }
This config sets up a default connection to APNS servers.
cert
andkey
can be any of the following:- Static file path
- Full-text string of the file contents
{:my_app, "certs/cert.pem"}
(indicates path relative to thepriv
folder of the given application)
Alternatively, you can use token based authentication:
config :pigeon, :apns, apns_default: %{ key: "AuthKey.p8", key_identifier: "ABC1234567", team_id: "DEF8901234", mode: :dev }
:key
- Created and downloaded via your developer account. Like:cert
this can be a file path, file contents string or tuple:key_identifier
- The 10-character key identifier associated with:key
, obtained from your developer account:team_id
- Your 10-character Team ID, obtained from your developer account
-
Create a notification packet. Note: Your push topic is generally the app's bundle identifier.
iex> n = Pigeon.APNS.Notification.new("your message", "your device token", "your push topic (optional)")
-
Send the packet. Pushes are synchronous and return the notification with an updated
:response
key.iex> Pigeon.APNS.push(n) %Pigeon.APNS.Notification{device_token: "your device token", expiration: nil, id: "963B9FDA-EA60-E869-AAB5-9C88C8E7396B", payload: %{"aps" => %{"alert" => "your message"}}, response: :success, topic: "your push topic"} # Add an `:on_response` callback for async pushes. iex> Pigeon.APNS.push(n, on_response: fn(x) -> IO.inspect(x) end) :ok
Additional documentation: APNS (Apple iOS)
Looking for GCM? Try v0.13
or earlier.
-
Add a default worker config to your mix config.
config :pigeon, :fcm, fcm_default: %{ key: "your_fcm_key_here" }
-
Create a notification packet. FCM notifications support
iex> msg = %{ "body" => "your message" } iex> n = Pigeon.FCM.Notification.new("your device registration ID", msg)
-
Send the packet. Pushes are synchronous and return the notification with updated
:status
and:response
keys. If:status
is success,:response
will contain a keyword list of individual registration ID responses.iex> Pigeon.FCM.push(n) %Pigeon.FCM.Notification{message_id: "0:1512580747839227%8911a9178911a917", payload: %{"notification" => %{"body" => "your message"}}, priority: :normal, registration_id: "your device registration ID", response: [success: "your device registration ID"], status: :success} # Add an `:on_response` callback for async pushes. iex> Pigeon.FCM.push(n, on_response: fn(x) -> IO.inspect(x) end) :ok
Additional documentation: FCM (Android)
-
Add a default worker config to your mix config.
config :pigeon, :adm, adm_default: %{ client_id: "your_oauth2_client_id_here", client_secret: "your_oauth2_client_secret_here" }
-
Create a notification packet.
iex> msg = %{ "body" => "your message" } iex> n = Pigeon.ADM.Notification.new("your device registration ID", msg)
-
Send the packet.
iex> Pigeon.ADM.push(n) %Pigeon.ADM.Notification{consolidation_key: nil, expires_after: 604800, md5: "M13RuG4uDWqajseQcCiyiw==", payload: %{"data" => %{"body" => "your message"}}, registration_id: "your device registration ID", response: :success, updated_registration_id: nil} # Add an `:on_response` callback for async pushes. iex> Pigeon.ADM.push(n, on_response: fn(x) -> IO.inspect(x) end) :ok
Additional documentation: ADM (Amazon Android)
Workers may be specified at application startup by creating a module containing functions that return zero or more configuration structures appropriate to the push service being enabled.
Specify these in your config.exs
as:
config :pigeon, workers: [
{YourApp.Pigeon, :apns_config},
{YourApp.Pigeon, :fcm_config},
{YourApp.Pigeon, :adm_config}
]
These should be implemented as:
defmodule YourApp.Pigeon do
@moduledoc false
@push_mode if(Mix.env() == :production, do: :prod, else: :dev)
def apns_config do
Pigeon.APNS.ConfigParser.parse(
key: System.get_env("APNS_KEY"),
key_identifier: System.get_env("APNS_KEY_ID"),
team_id: System.get_env("APNS_TEAM_ID"),
mode: @push_mode,
name: :apns_default
)
end
def fcm_config do
Pigeon.FCM.Config.new(name: :fcm_default, key: System.get_env("FCM_SERVER_KEY"))
end
end
If your startup configuration requires reading your configuration from a database or using another dependency that needs a database, startup is a little more complex.
-
Modify your
mix.exs
to not startpigeon
by default:def deps do [ {:pigeon, "~> 1.3.1", runtime: false}, {:kadabra, "~> 0.4.4"}, {:ecto, "~> 2.0 or ~> 3.0"} ] end
-
Modify
config.exs
to specify a single worker function:config :pigeon, workers: [{YourApp.Pigeon, :config}]
-
Modify your main application to start
pigeon
after yourRepo
has been started under your application’s supervision tree:def start(_type, _args) do children = [ YourApp.Repo, ] opts = [strategy: :one_for_one, name: YourApp.Supervisor] with {:ok, sup} <- Supervisor.start_link(children, opts), {:ok, _} <- Application.ensure_all_started(:pigeon, :permanent) do {:ok, sup} end end
-
Implement your database query as part of your Pigeon module:
defmodule YourApp.Pigeon do @moduledoc false alias YourApp.{PushApplication, Repo} def config_workers do PushApplication |> Repo.all() |> Enum.map(&build_config/1) end defp build_config(%{type: "apns"} = config) Pigeon.APNS.ConfigParser.parse( key: config.key, key_identifier: config.key_identifier, team_id: config.team_id, mode: config.mode, name: Atom.to_string(config.name) # This is bad, but keep it simple! ) end defp build_config(%{type: "fcm"} = config) do Pigeon.FCM.Config.new( name: Atom.to_string(config.name), key: config.key ) end end
Unit tests can be run with mix test
or mix coveralls.html
.
This project uses Elixir's mix format
for formatting. Add a hook in your editor of choice to
run it after a save. Be sure it respects this project's .formatter.exs
.
Git commit subjects use the Karma style.