Install apns2 via pip
masterYou can install the apns2 library directly from PyPI using pip.
$ pip install apns2repository·master·Indexed 18 days ago
https://github.com/pr0ger/pyapns2A Python library for interacting with the Apple Push Notification service (APNs) via the HTTP/2 protocol. It supports both certificate-based and token-based (JWT) authentication, allowing for the transmission of single notifications or efficient batches using HTTP/2 streams. The library includes tools for constructing payloads via Payload and PayloadAlert classes, managing notification priorities and types, and handling APNs-specific communication errors.
You can install the apns2 library directly from PyPI using pip.
$ pip install apns2To send a single notification, use APNsClient with a certificate file and the send_notification method. You must provide the device token (hex string), a Payload object, and the topic (your app's bundle ID).
from apns2.client import APNsClient
from apns2.payload import Payload
token_hex = 'b5bb9d8014a0f9b1d61e21e796d78dccdf1352f23cd32812f4850b87'
payload = Payload(alert="Hello World!", sound="default", badge=1)
topic = 'com.example.App'
# Initialize client with a certificate file
client = APNsClient('key.pem', use_sandbox=False, use_alternative_port=False)
# Send the notification
client.send_notification(token_hex, payload, topic)Instead of using a certificate file, you can use Apple's token-based authentication (JWT) by passing TokenCredentials to the APNsClient. This requires your authentication key path, the key ID, and your Team ID.
from apns2.client import APNsClient
from apns2.payload import Payload
from apns2.credentials import TokenCredentials
import collections
# Authentication configuration
auth_key_path = 'path/to/auth_key'
auth_key_id = 'app_auth_key_id'
t team_id = 'app_team_id'
# Setup notification data
token_hex = 'b5bb9d8014a0f9b1d61e21e796d78dccdf1352f23cd32812f4850b87'
payload = Payload(alert="Hello World!", sound="default", badge=1)
topic = 'com.example.App'
Notification = collections.namedtuple('Notification', ['token', 'payload'])
notifications = [Notification(payload=payload, token=token_hex)]
# Initialize client with TokenCredentials
token_credentials = TokenCredentials(auth_key_path=auth_key_path, auth_key_id=auth_key_id, team_id=team_id)
client = APNsClient(credentials=token_credentials, use_sandbox=False)
# Send batch
client.send_notification_batch(notifications=notifications, topic=topic)To send multiple notifications efficiently, use the send_notification_batch method. This method expects a list of notification objects. Each object should contain a token and a payload.
import collections
from apns2.client import APNsClient
from apns2.payload import Payload
# Setup data
token_hex = 'b5bb9d8014a0f9b1d61e21e796d78dccdf1352f23cd32812f4850b87'
payload = Payload(alert="Hello World!", sound="default", badge=1)
topic = 'com.example.App'
# Create a batch of notifications
Notification = collections.namedtuple('Notification', ['token', 'payload'])
notifications = [Notification(payload=payload, token=token_hex)]
client = APNsClient('key.pem', use_sandbox=False, use_alternative_port=False)
client.send_notification_batch(notifications=notifications, topic=topic)Both PayloadAlert and Payload provide a .dict() method to convert the object into the dictionary format required for transmission to APNs.
PayloadAlert.dict() returns a dictionary containing the alert's text and localization keys.Payload.dict() returns a dictionary where the core APNs keys are nested under the 'aps' key, and any custom dictionary provided is merged into the root level.from apns2.payload import Payload, PayloadAlert
alert = PayloadAlert(title="Hello")
payload = Payload(alert=alert, badge=1)
# Convert to dictionary for transmission
dict_representation = payload.dict()
# Result: {'aps': {'alert': {'title': 'Hello'}, 'badge': 1}}Use send_notification to send a single push notification and wait for the result. This method will raise an exception if the notification fails to be accepted by APNs.
token_hex: The device token in hexadecimal format.notification: A Payload object containing the notification content.topic (optional): The apns-topic (usually the bundle ID).priority (optional): A NotificationPriority enum (Immediate or Delayed).expiration (optional): The expiration time in seconds.collapse_id (optional): The apns-collapse-id to group notifications.from apns2.client import APNsClient
from apns2.payload import Payload
from apns2.credentials import CertificateCredentials
client = APNsClient(credentials=CertificateCredentials('cert.pem'))
payload = Payload(alert='Hello World')
client.send_notification(
token_hex='DEVICE_TOKEN_HEX',
notification=payload,
topic='com.example.app'
)The APNsClient is the primary interface for interacting with Apple Push Notification service (APNs). You can initialize it using either a Credentials object or a string path to a certificate file.
credentials: A Credentials instance or a str representing the path to a certificate file.use_sandbox (bool): If True, connects to the Apple development server (api.development.push.apple.com). Defaults to False (Live server).use_alternative_port (bool): If True, uses port 2197 instead of the default 443.heartbeat_period (float, optional): If provided, starts a background thread to send periodic pings to keep the connection alive.from apns2.client import APNsClient
from apns2.credentials import CertificateCredentials
# Using a Credentials object
creds = CertificateCredentials('path/to/cert.pem', password='your_password')
client = APNsClient(credentials=creds, use_sandbox=True)
# OR using a certificate file path directly
client = APNsClient(credentials='path/to/cert.pem', password='your_password', use_sandbox=True)If you receive a raw error reason string from the APNs response, you can use exception_class_for_reason(reason: str) to retrieve the corresponding APNsException class. This is useful for dynamic error handling based on string identifiers.
from apns2.errors import exception_class_for_reason
reason_str = 'BadDeviceToken'
exception_cls = exception_class_for_reason(reason_str)
# You can then raise it or use it for type checking
try:
raise exception_cls()
except Exception as e:
print(f"Caught expected exception: {type(e).__name__}")To send multiple notifications efficiently, use send_notification_batch. This method leverages HTTP/2 streams to send multiple requests concurrently over a single connection, significantly improving throughput.
It automatically manages concurrency by reading the SETTINGS frame from the APNs server to determine the maximum allowed concurrent streams.
notifications: An iterable of Notification namedtuples, where each tuple contains (token, payload).topic, priority, expiration, collapse_id, push_type: Same as send_notification.Returns a dictionary mapping each device token to its result. The result is either 'Success' or a string representing the failure reason (or a tuple (reason, timestamp) for Unregistered tokens).
from apns2.client import APNsClient, Notification
from apns2.payload import Payload
from apns2.credentials import CertificateCredentials
client = APNsClient(credentials=CertificateCredentials('cert.pem'))
# Create a list of Notification namedtuples
nots = [
Notification(token='TOKEN_1', payload=Payload(alert='Msg 1')),
Notification(token='TOKEN_2', payload=Payload(alert='Msg 2')),
]
results = client.send_notification_batch(notifications=nots, topic='com.example.app')
for token, result in results.items():
print(f"Token {token} result: {result}")PyAPNs2 uses a specific exception hierarchy to categorize errors encountered during communication with Apple Push Notification service (APNs). You can catch the base APNsException to handle all library-related errors, or catch specific subclasses to implement granular error handling (e.g., retrying on ServiceUnavailable or updating tokens on ExpiredProviderToken).
from apns2.errors import APNsException, Unregistered, ServiceUnavailable
try:
# Your APNs push logic here
pass
except Unregistered:
# Handle case where device token is no longer valid for the topic
pass
except ServiceUnavailable:
# Handle temporary APNs downtime
pass
except APNsException as e:
# Catch-all for other APNs related issues
print(f"An APNs error occurred: {e}")The following is a reference of the available exception classes in apns2/errors.py, grouped by their functional category.
# Base Exception
APNsException
# Connection & Server Errors
ConnectionFailed
InternalServerError
ServiceUnavailable
Shutdown
IdleTimeout
Forbidden
# Authentication & Token Errors
BadCertificate
BadCertificateEnvironment
ExpiredProviderToken
InvalidProviderToken
MissingProviderToken
TooManyProviderTokenUpdates
# Payload & Header Errors (BadPayloadException)
BadPayloadException
BadCollapseId
BadExpirationDate
BadTopic
MissingTopic
PayloadEmpty
TopicDisallowed
PayloadTooLarge
# Device & Topic Mapping Errors
BadDeviceToken
DeviceTokenNotForTopic
MissingDeviceToken
Unregistered
# Request & Protocol Errors
BadPath
TooManyRequests
# Internal/Bug Indicators (InternalException)
InternalException
BadMessageId
BadPriority
DuplicateHeaders
MethodNotAllowedUse the PayloadAlert class to define the visible content of a notification, such as the title, subtitle, and body text. It supports both direct strings and localization keys with arguments for multi-language support.
Parameters:
title: The main title of the alert.title_localized_key: The localization key for the title.title_localized_args: A list of arguments for the title localization.subtitle: The subtitle of the alert.subtitle_localized_key: The localization key for the subtitle.subtitle_localized_args: A list of arguments for the subtitle localization.body: The main body text of the alert.body_localized_key: The localization key for the body text (maps to loc-key in the final dictionary).body_localized_args: A list of arguments for the body localization (maps to loc-args).action: The text for the action button.action_localized_key: The localization key for the action button.launch_image: A URL or path to a launch image.from apns2.payload import PayloadAlert
alert = PayloadAlert(
title="Hello",
body="This is a notification",
action="View"
)