aioapns: Asynchronous APNs Client
aioapns is an efficient and asynchronous APNs (Apple Push Notification service) client library for Python, built on asyncio. It allows developers to send push notifications to iOS, macOS, watchOS, and tvOS devices. The current version is 4.0, and it generally follows a moderate release cadence, with major versions introducing significant changes.
Warnings
- breaking In v4.0, the `key` argument for `APNs` and `APNsKeyConnectionPool` classes changed from expecting a file path to expecting the *content* of the .p8 private key file as a string. Passing a path will now cause errors.
- breaking Support for Python 3.6 and 3.7 was dropped in v3.2. Attempting to use aioapns v3.2 or newer with these Python versions will result in compatibility issues or installation failures.
- gotcha Earlier versions (prior to v3.0) had a known issue with deadlocks following timeouts. While fixed in v3.0, users on older versions might experience unresponsiveness under certain network conditions.
- gotcha The `h2` library is a critical underlying dependency for HTTP/2 communication. While `aioapns` specifies `h2>=4.0.0`, conflicts with other libraries that might pin older `h2` versions can lead to unexpected behavior or runtime errors.
Install
-
pip install aioapns
Imports
- APNs
from aioapns import APNs
- NotificationRequest
from aioapns import NotificationRequest
Quickstart
import asyncio
import os
from aioapns import APNs, NotificationRequest
async def main():
# Ensure you have these environment variables set or replace with actual values
# APNS_KEY_CONTENT: The content of your .p8 private key file (NOT the path)
# APNS_KEY_ID: Your 10-character key ID from Apple Developer website
# APNS_TEAM_ID: Your 10-character Team ID from Apple Developer website
# APNS_TOPIC: Your app's bundle ID (e.g., com.example.app)
# APNS_DEVICE_TOKEN: A valid device token obtained from an iOS/macOS device
key_content = os.environ.get('APNS_KEY_CONTENT', '-----BEGIN PRIVATE KEY-----\nYOUR_P8_KEY_CONTENT_HERE\n-----END PRIVATE KEY-----')
key_id = os.environ.get('APNS_KEY_ID', 'YOUR_KEY_ID')
team_id = os.environ.get('APNS_TEAM_ID', 'YOUR_TEAM_ID')
topic = os.environ.get('APNS_TOPIC', 'com.example.app')
device_id = os.environ.get('APNS_DEVICE_TOKEN', 'your_device_token_here')
if 'YOUR_P8_KEY_CONTENT_HERE' in key_content or 'YOUR_KEY_ID' == key_id or 'YOUR_TEAM_ID' == team_id or 'com.example.app' == topic or 'your_device_token_here' == device_id:
print("Please set APNS_KEY_CONTENT, APNS_KEY_ID, APNS_TEAM_ID, APNS_TOPIC, and APNS_DEVICE_TOKEN environment variables or replace placeholders in the code.")
return
try:
apns_client = APNs(
key=key_content, # For v4.0+, this is the key content (string), not a path
key_id=key_id,
team_id=team_id,
topic=topic,
use_sandbox=True, # Set to False for production environment
)
request = NotificationRequest(
device_id=device_id,
message={
'aps': {
'alert': 'Hello from aioapns!',
'sound': 'default',
'badge': 1
},
'custom_data': 'some_value'
}
)
response = await apns_client.send_notification(request)
if response.is_successful:
print(f"Notification successfully sent to {device_id}!")
else:
print(f"Failed to send notification. Status: {response.status}, Reason: {response.reason}")
except Exception as e:
print(f"An error occurred: {e}")
if __name__ == '__main__':
asyncio.run(main())