{"id":20450877,"url":"https://github.com/danhper/pushex","last_synced_at":"2025-12-12T00:10:51.479Z","repository":{"id":57538053,"uuid":"54825292","full_name":"danhper/pushex","owner":"danhper","description":"Push notifications for Elixir","archived":false,"fork":false,"pushed_at":"2019-09-11T17:00:00.000Z","size":115,"stargazers_count":104,"open_issues_count":1,"forks_count":7,"subscribers_count":2,"default_branch":"master","last_synced_at":"2025-03-26T20:03:06.138Z","etag":null,"topics":["apns","elixir","gcm","notifications"],"latest_commit_sha":null,"homepage":"https://hexdocs.pm/pushex","language":"Elixir","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/danhper.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2016-03-27T11:27:35.000Z","updated_at":"2024-06-16T02:19:37.000Z","dependencies_parsed_at":"2022-09-07T16:50:26.619Z","dependency_job_id":null,"html_url":"https://github.com/danhper/pushex","commit_stats":null,"previous_names":["tuvistavie/pushex"],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/danhper%2Fpushex","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/danhper%2Fpushex/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/danhper%2Fpushex/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/danhper%2Fpushex/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/danhper","download_url":"https://codeload.github.com/danhper/pushex/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248657795,"owners_count":21140841,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["apns","elixir","gcm","notifications"],"created_at":"2024-11-15T10:55:29.954Z","updated_at":"2025-12-12T00:10:51.450Z","avatar_url":"https://github.com/danhper.png","language":"Elixir","funding_links":[],"categories":["Elixir"],"sub_categories":[],"readme":"# Pushex\n[![Build Status](https://travis-ci.org/danhper/pushex.svg?branch=master)](https://travis-ci.org/danhper/pushex)\n[![Coverage Status](https://coveralls.io/repos/github/danhper/pushex/badge.svg?branch=master)](https://coveralls.io/github/danhper/pushex?branch=master)\n\n\nPushex is a library to easily send push notifications with Elixir.\n\n## About\n\n### Goals\n\nThe main goals are the following:\n\n  * Easy to use async API\n  * Common API for iOS and Android\n  * Multiple applications handling\n  * Proper error and response handling\n  * Testable using a sanbox mode\n\n### Status\n\nBoth GCM and APNS are working. APNS delegates to [apns4ex](https://github.com/chvanikoff/apns4ex)\nfor now, I will probably use the HTTP2 API in a later version.\n\nThe API is still subject to change, with a minor version bump for each change.\n\n## Installation\n\nAdd the following to your dependencies mix.ex.\n\n```elixir\n[{:pushex, \"~\u003e 0.2\"}]\n```\n\nThen, add `:pushex` to your applications.\n\n## Usage\n\nThe most basic usage, with no configuration looks like this:\n\n```elixir\napp = %Pushex.GCM.App{name: \"a_unique_name_you_like\", auth_key: \"a GCM API auth key\"}\nPushex.push(%{title: \"my_title\", body: \"my_body\"}, to: \"registration_id\", with_app: app)\n```\n\nTo avoid having to create or retreive your app each time, you can configure as many apps\nas you want in your `config.exs`:\n\n```elixir\nconfig :pushex,\n  gcm: [\n    default_app: \"first_app\",\n    apps: [\n      [name: \"first_app\", auth_key: \"a key\"],\n      [name: \"other_app\", auth_key: \"another key\"]\n    ]\n  ],\n  apns: [\n    default_app: \"first_app\",\n    apps: [\n      [name: \"first_app\", env: :dev, certfile: \"/path/to/certfile\", pool_size: 5]\n    ]\n  ]\n```\n\nYou can then do the following:\n\n\n```elixir\n# this will use the default app, \"first_app\" with the above configuration\nPushex.push(%{title: \"my_title\", body: \"my_body\"}, to: \"registration_id\", using: :gcm)\n\n# this will use the other_app\nPushex.push(%{title: \"my_title\", body: \"my_body\"}, to: \"registration_id\", using: :gcm, with_app: \"other_app\")\n```\n\nNote that the function is async and only returns a reference, see the response and error\nhandling documentation for more information.\n\n### Sending to multiple platforms\n\nIf you want to use the same message for both platforms, you can define messages as follow:\n\n```elixir\nmessage = %{\n  common: \"this will be in both payloads\",\n  other: \"this will also be in both payloads\",\n  apns: %{\n    alert: \"My alert\",\n    badge: 1\n  },\n  gcm: %{\n    title: \"GCM title\",\n    body: \"My body\"\n  }\n}\n\nPushex.push(message, to: [\"apns_token1\", \"apns_token2\"], using: :apns)\nPushex.push(message, to: [\"gcm_registration_id1\", \"gcm_registration_id2\"], using: :gcm)\n```\n\nOnly `:gcm` and `:apns` are currently available.\n\n### Passing more options\n\nIf you need to pass options, `priority` for example, you can just pass\nit in the keyword list and it will be sent.\n\nSee\n\nhttps://developers.google.com/cloud-messaging/http-server-ref#downstream-http-messages-json\n\nfor more information.\n\nThe parameters from `Table 1` should be passed in the keyword list, while\nthe parameters from `Table 2` should be passed in the first argument.\n\nFor more information about `APNS` options, see [apns4ex](https://github.com/chvanikoff/apns4ex) docs.\n\nNOTE: if you pass an array to the `to` parameter, if will automatically\nbe converted to `registration_ids` when sending the request, to keep a consistent API.\n\n### Loading app from somewhere else\n\nIf you are saving your auth_keys in your database, you can override the default way to retreive the apps:\n\n```elixir\n# config.exs\nconfig :pushex,\n  app_manager_impl: MyAppManager\n\n# my_app_manager.ex\ndefmodule MyAppManager do\n  @behaviour Pushex.AppManager\n\n  def find_app(platform, name) do\n    if app = Repo.get_by(App, platform: platform, name: name) do\n      make_app(platform, app)\n    end\n  end\n\n  # transform to a `Pushex._.App`\n  defp make_app(:gcm, app) do\n    struct(Pushex.GCM.App, Map.from_struct(app))\n  end\n  defp make_app(:apns, app) do\n    struct(Pushex.APNS.App, Map.from_struct(app))\n  end\nend\n```\n\n### Handling responses\n\nTo handle responses, you can define a module using `Pushex.EventHandler`\nwhich uses `:gen_event` to process events.\n\n```elixir\n# config.exs\nconfig :pushex,\n  event_handlers: [MyEventHandler]\n\n# my_event_handler.ex\ndefmodule MyEventHandler do\n  use Pushex.EventHandler\n\n  def handle_event({:request, request, {pid, ref}}, state) do\n    # do whatever you want with the request\n    # for example, logging or saving in a DB\n    {:ok, state}\n  end\n\n  def handle_event({:response, response, request, {pid, ref}}, state) do\n    # do whatever you want with the response and request\n    {:ok, state}\n  end\nend\n```\n\nThe `ref` passed here is the one returned when calling `push`.\n\n## Testing\n\nPushex offers a sandbox mode to make testing easier.\n\nTo enable it, you should add the following to your configuration:\n\n```\nconfig :pushex,\n  sandbox: true\n```\n\nOnce you are using the sandbox, the messages will not be sent to GCM or APNS anymore,\nbut stored in `Pushex.Sandbox`. Furthermore, all the messages will be returned\nto the process that sent them.\nHere is a sample test.\n\n```elixir\ntest \"send notification to users\" do\n  ref = Pushex.push(%{body: \"my message\"}, to: \"my-user\", using: :gcm)\n  pid = self()\n  assert_receive {{:ok, response}, request, ^ref}\n  assert [{{:ok, ^response}, ^request, {^pid, ^ref}}] = Pushex.Sandbox.list_notifications\nend\n```\n\nNote that `list_notifications` depends on the running process, so\nif you call it from another process, you need to explicitly pass the pid with the `:pid` option.\n\nAlso note that `Pushex.push` is asynchronous, so if you\nremove the `assert_receive`, you will have a race condition.\nTo avoid this, you can use `Pushex.Sandbox.wait_notifications/1` instead of `Pushex.Sandbox.list_notifications`.\nIt will wait (by default for `100ms`) until at least `:count` notifications arrive\n\n```elixir\ntest \"send notification to users and wait\" do\n  Enum.each (1..10), fn _ -\u003e\n    Pushex.push(%{body: \"foo\"}, to: \"whoever\", using: :gcm)\n  end\n  notifications = Pushex.Sandbox.wait_notifications(count: 10, timeout: 50)\n  assert length(notifications) == 10\nend\n```\n\nHowever, the requests are asynchronous, so there is no guaranty that the notifications\nin the sandbox will in the same order they have been sent.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdanhper%2Fpushex","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fdanhper%2Fpushex","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdanhper%2Fpushex/lists"}