apns2 Python Library

repository·master·Indexed 18 days ago

https://github.com/pr0ger/pyapns2

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

Tokens
5.4K
Snippets
19
Records
19
Agent score
62%

What's inside apns2

  1. Send a single push notification

    master

    To 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)
  2. Use token-based authentication

    master

    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)
  3. Send multiple notifications in a batch

    master

    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)
  4. Convert payloads to dictionaries using .dict()

    master

    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}}
  5. Send a single notification synchronously

    master

    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.

    Parameters

    • 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'
    )
  6. Initialize the APNsClient

    master

    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.

    Parameters

    • 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)
  7. Map error reason strings to exception classes

    master

    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__}")
  8. Send notifications in batch using HTTP/2 streams

    master

    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.

    Parameters

    • notifications: An iterable of Notification namedtuples, where each tuple contains (token, payload).
    • topic, priority, expiration, collapse_id, push_type: Same as send_notification.

    Returns

    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}")
  9. Handle APNs communication and payload errors

    master

    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}")
  10. Reference: APNs Exception Hierarchy

    master

    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
    MethodNotAllowed
  11. Construct notification alerts with PayloadAlert

    master

    Use 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"
    )