Apple Push Notification Service (APNS) client.
Sends push notifications to iOS, macOS, watchOS, tvOS devices, and Safari using HTTP/2 and JWT-based authentication.
Configuration
Add to your config:
config :pushx,
apns_key_id: "ABC123DEFG",
apns_team_id: "TEAM123456",
apns_private_key: {:file, "priv/keys/AuthKey.p8"},
apns_mode: :prod # or :sandboxUsage
# Simple notification
PushX.APNS.send(device_token, %{
"aps" => %{
"alert" => %{"title" => "Hello", "body" => "World"},
"sound" => "default"
}
}, topic: "com.example.app")
# Using Message struct
message = PushX.Message.new("Hello", "World")
PushX.APNS.send(device_token, message, topic: "com.example.app")Safari Web Push
Safari uses APNS for web push notifications. The token format is the same
as iOS (64 hex characters), but the topic uses a web. prefix:
# Safari web push
PushX.APNS.send(safari_token, payload, topic: "web.com.example.website")
# Using web notification helper
payload = PushX.APNS.web_notification("Title", "Body", "https://example.com/page")
PushX.APNS.send(safari_token, payload, topic: "web.com.example.website")
Summary
Functions
Creates a simple notification payload.
Creates a notification with custom data.
Sends a push notification to an iOS device with automatic retry.
Sends notifications to multiple devices concurrently.
Sends a push notification without retry.
Creates a silent/background notification.
Creates a Safari web push notification payload.
Creates a Safari web push notification with custom data.
Types
@type option() :: {:topic, String.t()} | {:mode, :prod | :sandbox} | {:push_type, String.t()} | {:priority, 5 | 10} | {:expiration, non_neg_integer()} | {:collapse_id, String.t()}
@type payload() :: map() | PushX.Message.t()
@type token() :: String.t()
Functions
@spec notification(String.t(), String.t(), non_neg_integer() | nil) :: map()
Creates a simple notification payload.
Examples
iex> PushX.APNS.notification("Hello", "World")
%{"aps" => %{"alert" => %{"title" => "Hello", "body" => "World"}, "sound" => "default"}}
@spec notification_with_data(String.t(), String.t(), map(), non_neg_integer() | nil) :: map()
Creates a notification with custom data.
@spec send(token(), payload(), [option()]) :: {:ok, PushX.Response.t()} | {:error, PushX.Response.t()}
Sends a push notification to an iOS device with automatic retry.
Uses exponential backoff for transient failures following Apple's best practices. Permanent failures (bad token, payload too large) are not retried.
Options
:topic- Bundle ID (required):mode-:prodor:sandbox(default: from config):push_type- "alert", "background", "voip", etc. (default: "alert"):priority- 5 or 10 (default: 10):expiration- Unix timestamp when notification expires:collapse_id- Group notifications with the same ID:retry- Enable/disable retry (default: true from config)
Returns
{:ok, %PushX.Response{}}on success{:error, %PushX.Response{}}on failure
@spec send_batch([token()], payload(), [option()]) :: [ {token(), {:ok, PushX.Response.t()} | {:error, PushX.Response.t()}} ]
Sends notifications to multiple devices concurrently.
Options
All standard options plus:
:concurrency- Max concurrent requests (default: 50):timeout- Timeout per request in ms (default: 30_000)
Returns
A list of {token, result} tuples.
@spec send_once(token(), payload(), [option()]) :: {:ok, PushX.Response.t()} | {:error, PushX.Response.t()}
Sends a push notification without retry.
Use this when you want to handle retries yourself or for testing.
Creates a silent/background notification.
Creates a Safari web push notification payload.
Safari web push uses APNS with a slightly different payload format.
The url-args field is used to pass URL arguments to the notification action.
Arguments
title- Notification titlebody- Notification bodyurl- URL to open when clicked (or URL arguments for Safari)opts- Optional keyword list::action- Action button label (default: "View"):url_args- List of URL arguments (overrides url parsing)
Examples
# Simple web notification
PushX.APNS.web_notification("New Article", "Check out our latest post", "https://example.com/article/123")
# With custom action
PushX.APNS.web_notification("Sale!", "50% off today", "https://shop.com", action: "Shop Now")
# With explicit URL args
PushX.APNS.web_notification("Update", "New feature available", nil, url_args: ["features", "v2"])
@spec web_notification_with_data( String.t(), String.t(), String.t() | nil, map(), keyword() ) :: map()
Creates a Safari web push notification with custom data.
Examples
PushX.APNS.web_notification_with_data(
"Order Shipped",
"Your order #123 is on its way",
"https://example.com/orders/123",
%{"order_id" => "123"}
)