PushX.APNS (PushX v0.5.0)

Copy Markdown View Source

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 :sandbox

Usage

# 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

option()

@type option() ::
  {:topic, String.t()}
  | {:mode, :prod | :sandbox}
  | {:push_type, String.t()}
  | {:priority, 5 | 10}
  | {:expiration, non_neg_integer()}
  | {:collapse_id, String.t()}

payload()

@type payload() :: map() | PushX.Message.t()

token()

@type token() :: String.t()

Functions

notification(title, body, badge \\ nil)

@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"}}

notification_with_data(title, body, data, badge \\ nil)

@spec notification_with_data(String.t(), String.t(), map(), non_neg_integer() | nil) ::
  map()

Creates a notification with custom data.

send(device_token, payload, opts \\ [])

@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 - :prod or :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

send_batch(device_tokens, payload, opts \\ [])

@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.

send_once(device_token, payload, opts \\ [])

@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.

silent_notification(data \\ %{})

@spec silent_notification(map()) :: map()

Creates a silent/background notification.

web_notification(title, body, url \\ nil, opts \\ [])

@spec web_notification(String.t(), String.t(), String.t() | nil, keyword()) :: map()

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 title
  • body - Notification body
  • url - 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"])

web_notification_with_data(title, body, url, data, opts \\ [])

@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"}
)