PyAPNs
Python library for interacting with the Apple Push Notification service (APNs)
apns 2.0.1 : Python Package Index a python library for interacting with the apple push notification service
I am using PyAPNs for sending iOS Push Notification. I also merged the fixes for following known issues
https://github.com/djacobs/PyAPNs/issues/13
Now, the code is working fine If I send notification to an individual device. But I have a list for device tokens and I have to send notification to all of them one by one. For this purpose I am simple looping over the single notification call like this:
def send_notifications(self, tokens, payload):
for token in tokens:
try :
logging.info("Sending Notification to Token: %s" % (token))
self.send_notification(token, payload)
except Exception, e:
self._disconnect()
logging.info("Exception: %s" % (str(e)))
logging.info("Token: %s" % (token))
But the problem is that above code is not working. The device token which was working fine for individual push, is not working using above code. For example device token 45183e79de216ea05e3d6e83083476ebeb64caf733188bb77b0b1d268526c815 is working fine individually but failing in case of bulk send. For reference I am putting the apns file and partial server logs:
apns.py
# PyAPNs was developed by Simon Whitaker <simon@goosoftware.co.uk>
# Source available at https://github.com/simonwhitaker/PyAPNs
#
# PyAPNs is distributed under the terms of the MIT license.
#
# Copyright (c) 2011 Goo Software Ltd
#
# Permission is hereby granted, free of charge, to any person obtaining a copy of
# this software and associated documentation files (the "Software"), to deal in
# the Software without restriction, including without limitation the rights to
# use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
# of the Software, and to permit persons to whom the Software is furnished to do
# so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in all
# copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.
from binascii import a2b_hex, b2a_hex
from datetime import datetime, timedelta
from time import mktime
from socket import socket, AF_INET, SOCK_STREAM, timeout
from struct import pack, unpack
import select
try:
from ssl import wrap_socket
from ssl import SSLError, SSL_ERROR_WANT_READ, SSL_ERROR_WANT_WRITE
except ImportError:
from socket import ssl as wrap_socket
try:
import json
except ImportError:
import simplejson as json
from apnserrors import *
import logging
import StringIO
MAX_PAYLOAD_LENGTH = 256
TIMEOUT = 60
ERROR_RESPONSE_LENGTH = 6
class APNs(object):
"""A class representing an Apple Push Notification service connection"""
def __init__(self, use_sandbox=False, cert_file=None, key_file=None, enhanced=True):
"""
Set use_sandbox to True to use the sandbox (test) APNs servers.
Default is False.
"""
super(APNs, self).__init__()
self.use_sandbox = use_sandbox
self.cert_file = cert_file
self.key_file = key_file
self.enhanced = enhanced
self._feedback_connection = None
self._gateway_connection = None
@staticmethod
def unpacked_uchar_big_endian(byte):
"""
Returns an unsigned char from a packed big-endian (network) byte
"""
return unpack('>B', byte)[0]
@staticmethod
def packed_ushort_big_endian(num):
"""
Returns an unsigned short in packed big-endian (network) form
"""
return pack('>H', num)
@staticmethod
def unpacked_ushort_big_endian(bytes):
"""
Returns an unsigned short from a packed big-endian (network) byte
array
"""
return unpack('>H', bytes)[0]
@staticmethod
def packed_uint_big_endian(num):
"""
Returns an unsigned int in packed big-endian (network) form
"""
return pack('>I', num)
@staticmethod
def unpacked_uint_big_endian(bytes):
"""
Returns an unsigned int from a packed big-endian (network) byte array
"""
return unpack('>I', bytes)[0]
@property
def feedback_server(self):
if not self._feedback_connection:
self._feedback_connection = FeedbackConnection(
use_sandbox = self.use_sandbox,
cert_file = self.cert_file,
key_file = self.key_file
)
return self._feedback_connection
@property
def gateway_server(self):
if not self._gateway_connection:
self._gateway_connection = GatewayConnection(
use_sandbox = self.use_sandbox,
cert_file = self.cert_file,
key_file = self.key_file,
enhanced = self.enhanced
)
return self._gateway_connection
class APNsConnection(object):
"""
A generic connection class for communicating with the APNs
"""
def __init__(self, cert_file=None, key_file=None, enhanced=True):
super(APNsConnection, self).__init__()
self.cert_file = cert_file
self.key_file = key_file
self.enhanced = enhanced
self._socket = None
self._ssl = None
def __del__(self):
self._disconnect();
def _connect(self):
# Establish an SSL connection
self._socket = socket(AF_INET, SOCK_STREAM)
self._socket.connect((self.server, self.port))
if self.enhanced:
self._ssl = wrap_socket(self._socket, StringIO.StringIO(self.key_file), StringIO.StringIO(self.cert_file),
do_handshake_on_connect=False)
self._ssl.setblocking(0)
while True:
try:
self._ssl.do_handshake()
break
except SSLError, err:
if SSL_ERROR_WANT_READ == err.args[0]:
select.select([self._ssl], [], [])
elif SSL_ERROR_WANT_WRITE == err.args[0]:
select.select([], [self._ssl], [])
else:
raise
else:
self._ssl = wrap_socket(self._socket, StringIO.StringIO(self.key_file), StringIO.StringIO(self.cert_file))
def _disconnect(self):
if self._socket:
self._socket.close()
self._ssl = None
def _connection(self):
if not self._ssl:
self._connect()
return self._ssl
def read(self, n=None):
return self._connection().recv(n)
def recvall(self, n):
data = ""
while True:
more = self._connection().recv(n - len(data))
data += more
if len(data) >= n:
break
rlist, _, _ = select.select([self._connection()], [], [], TIMEOUT)
if not rlist:
raise timeout
return data
def write(self, string):
if self.enhanced: # nonblocking socket
rlist, _, _ = select.select([self._connection()], [], [], 0)
if rlist: # there's error response from APNs
buff = self.recvall(ERROR_RESPONSE_LENGTH)
if len(buff) != ERROR_RESPONSE_LENGTH:
return None
command = APNs.unpacked_uchar_big_endian(buff[0])
if 8 != command:
self._disconnect()
raise UnknownError(0)
status = APNs.unpacked_uchar_big_endian(buff[1])
identifier = APNs.unpacked_uint_big_endian(buff[2:6])
self._disconnect()
raise { 1: ProcessingError,
2: MissingDeviceTokenError,
3: MissingTopicError,
4: MissingPayloadError,
5: InvalidTokenSizeError,
6: InvalidTopicSizeError,
7: InvalidPayloadSizeError,
8: InvalidTokenError }.get(status, UnknownError)(identifier)
_, wlist, _ = select.select([], [self._connection()], [], TIMEOUT)
if wlist:
return self._connection().sendall(string)
else:
self._disconnect()
raise timeout
else: # not-enhanced format using blocking socket
return self._connection().sendall(string)
class PayloadAlert(object):
def __init__(self, body, action_loc_key=None, loc_key=None,
loc_args=None, launch_image=None):
super(PayloadAlert, self).__init__()
self.body = body
self.action_loc_key = action_loc_key
self.loc_key = loc_key
self.loc_args = loc_args
self.launch_image = launch_image
def dict(self):
d = { 'body': self.body }
if self.action_loc_key:
d['action-loc-key'] = self.action_loc_key
if self.loc_key:
d['loc-key'] = self.loc_key
if self.loc_args:
d['loc-args'] = self.loc_args
if self.launch_image:
d['launch-image'] = self.launch_image
return d
class Payload(object):
"""A class representing an APNs message payload"""
def __init__(self, alert=None, badge=None, sound=None, custom={}):
super(Payload, self).__init__()
self.alert = alert
self.badge = badge
self.sound = sound
self.custom = custom
self._check_size()
def dict(self):
"""Returns the payload as a regular Python dictionary"""
d = {}
if self.alert:
# Alert can be either a string or a PayloadAlert
# object
if isinstance(self.alert, PayloadAlert):
d['alert'] = self.alert.dict()
else:
d['alert'] = self.alert
if self.sound:
d['sound'] = self.sound
if self.badge is not None:
d['badge'] = int(self.badge)
d = { 'aps': d }
d.update(self.custom)
return d
def json(self):
return json.dumps(self.dict(), separators=(',',':'), ensure_ascii=False).encode('utf-8')
def _check_size(self):
if len(self.json()) > MAX_PAYLOAD_LENGTH:
raise PayloadTooLargeError()
def __repr__(self):
attrs = ("alert", "badge", "sound", "custom")
args = ", ".join(["%s=%r" % (n, getattr(self, n)) for n in attrs])
return "%s(%s)" % (self.__class__.__name__, args)
class FeedbackConnection(APNsConnection):
"""
A class representing a connection to the APNs Feedback server
"""
def __init__(self, use_sandbox=False, **kwargs):
super(FeedbackConnection, self).__init__(**kwargs)
self.server = (
'feedback.push.apple.com',
'feedback.sandbox.push.apple.com')[use_sandbox]
self.port = 2196
def _chunks(self):
BUF_SIZE = 4096
while 1:
data = self.read(BUF_SIZE)
yield data
if not data:
break
def items(self):
"""
A generator that yields (token_hex, fail_time) pairs retrieved from
the APNs feedback server
"""
buff = ''
for chunk in self._chunks():
buff += chunk
# Quit if there's no more data to read
if not buff:
break
# Sanity check: after a socket read we should always have at least
# 6 bytes in the buffer
if len(buff) < 6:
break
while len(buff) > 6:
token_length = APNs.unpacked_ushort_big_endian(buff[4:6])
bytes_to_read = 6 + token_length
if len(buff) >= bytes_to_read:
fail_time_unix = APNs.unpacked_uint_big_endian(buff[0:4])
fail_time = datetime.utcfromtimestamp(fail_time_unix)
token = b2a_hex(buff[6:bytes_to_read])
yield (token, fail_time)
# Remove data for current token from buffer
buff = buff[bytes_to_read:]
else:
# break out of inner while loop - i.e. go and fetch
# some more data and append to buffer
break
class GatewayConnection(APNsConnection):
"""
A class that represents a connection to the APNs gateway server
"""
def __init__(self, use_sandbox=False, **kwargs):
super(GatewayConnection, self).__init__(**kwargs)
self.server = (
'gateway.push.apple.com',
'gateway.sandbox.push.apple.com')[use_sandbox]
self.port = 2195
def _get_notification(self, token_hex, payload):
"""
Takes a token as a hex string and a payload as a Python dict and sends
the notification
"""
token_bin = a2b_hex(token_hex)
token_length_bin = APNs.packed_ushort_big_endian(len(token_bin))
payload_json = payload.json()
payload_length_bin = APNs.packed_ushort_big_endian(len(payload_json))
notification = ('\0' + token_length_bin + token_bin
+ payload_length_bin + payload_json)
return notification
def _get_enhanced_notification(self, token_hex, payload, identifier, expiry):
"""
Takes a token as a hex string and a payload as a Python dict and sends
the notification in the enhanced format
"""
token_bin = a2b_hex(token_hex)
token_length_bin = APNs.packed_ushort_big_endian(len(token_bin))
payload_json = payload.json()
payload_length_bin = APNs.packed_ushort_big_endian(len(payload_json))
identifier_bin = APNs.packed_uint_big_endian(identifier)
expiry_bin = APNs.packed_uint_big_endian(int(mktime(expiry.timetuple())))
notification = ('\1' + identifier_bin + expiry_bin + token_length_bin + token_bin
+ payload_length_bin + payload_json)
return notification
def send_notification(self, token_hex, payload, identifier=None, expiry=None):
if self.enhanced:
if not expiry: # by default, undelivered notification expires after 30 seconds
expiry = datetime.utcnow() + timedelta(30)
if not identifier:
identifier = 0
logging.info("self.write(self._get_enhanced_notification())")
self.write(self._get_enhanced_notification(token_hex, payload, identifier,
expiry))
else:
logging.info("self.write(self._get_notification(token_hex, payload))")
self.write(self._get_notification(token_hex, payload))
def send_notifications(self, tokens, payload):
for token in tokens:
try :
logging.info("Sending Notification to Token: %s" % (token))
self.send_notification(token, payload)
except Exception, e:
self._disconnect()
logging.info("Exception: %s" % (str(e)))
logging.info("Token: %s" % (token))
Server Logs:
Sending Notification to Token: 99f65209a76ed41ce50c73198d72048f94085dd2a2dde0245110dccccda86fd0
I 2014-05-20 05:18:24.029
self.write(self._get_enhanced_notification())
I 2014-05-20 05:18:24.437
Sending Notification to Token: 2230c2421e3b83cd6b16a69c6ba528230b11d29183b0bfb73b159816237b17ce
I 2014-05-20 05:18:24.437
self.write(self._get_enhanced_notification())
I 2014-05-20 05:18:24.442
.
.
.
Sending Notification to Token: 6bd80feb5158a8f92537955c93acd1661242c007dcffebe55e77bb38cafef0ba
I 2014-05-20 05:18:24.986
self.write(self._get_enhanced_notification())
I 2014-05-20 05:18:24.991
Sending Notification to Token: 2230c2421e3b83cd6b16a69c6ba528230b11d29183b0bfb73b159816237b17ce
I 2014-05-20 05:18:24.991
self.write(self._get_enhanced_notification())
I 2014-05-20 05:18:24.996
Sending Notification to Token: 6bd80feb5158a8f92537955c93acd1661242c007dcffebe55e77bb38cafef0ba
I 2014-05-20 05:18:24.996
self.write(self._get_enhanced_notification())
I 2014-05-20 05:18:25.004
Sending Notification to Token: 1bacfcb6b80868493b236ec6131bed11918c935752734701b89b060045e6b006
I 2014-05-20 05:18:25.004
self.write(self._get_enhanced_notification())
I 2014-05-20 05:18:25.021
Sending Notification to Token: 35bd8dda849e30a85b12b2a0e274b9507db7c7f365aa5a27f3fbda316052246e
I 2014-05-20 05:18:25.021
self.write(self._get_enhanced_notification())
I 2014-05-20 05:18:25.054
Sending Notification to Token: 6bd80feb5158a8f92537955c93acd1661242c007dcffebe55e77bb38cafef0ba
I 2014-05-20 05:18:25.054
self.write(self._get_enhanced_notification())
I 2014-05-20 05:18:25.059
Sending Notification to Token: 6bd80feb5158a8f92537955c93acd1661242c007dcffebe55e77bb38cafef0ba
I 2014-05-20 05:18:25.059
self.write(self._get_enhanced_notification())
I 2014-05-20 05:18:25.064
Sending Notification to Token: 1bacfcb6b80868493b236ec6131bed11918c935752734701b89b060045e6b006
I 2014-05-20 05:18:25.064
self.write(self._get_enhanced_notification())
I 2014-05-20 05:18:25.068
Sending Notification to Token: 6bd80feb5158a8f92537955c93acd1661242c007dcffebe55e77bb38cafef0ba
I 2014-05-20 05:18:25.069
self.write(self._get_enhanced_notification())
I 2014-05-20 05:18:25.073
Sending Notification to Token: d25a34a1fd031abf3fbfb5916af415206048fb6343586b91b96d0506eb28cb54
I 2014-05-20 05:18:25.073
self.write(self._get_enhanced_notification())
I 2014-05-20 05:18:25.078
.
.
.
Sending Notification to Token: 45183e79de216ea05e3d6e83083476ebeb64caf733188bb77b0b1d268526c815
I 2014-05-20 05:18:30.145
self.write(self._get_enhanced_notification())
I 2014-05-20 05:18:30.152
Sending Notification to Token: b57b2d96a4b4db552137bcea4fd58f3ce53393fbe7c828b617306df2922dbfd3
I 2014-05-20 05:18:30.152
self.write(self._get_enhanced_notification())
I 2014-05-20 05:18:30.159
Sending Notification to Token: 82acbf3dc5da893d2f4d551df10c129c8c192efe335cc608d291dc922e947615
I 2014-05-20 05:18:30.159
self.write(self._get_enhanced_notification())
I 2014-05-20 05:18:30.166
feedback token_hex: 0cf58d47f435f170473b63e1852b637c11935b6e38d41321fe98911eaf898301
I 2014-05-20 05:18:31.754
feedback token_hex: 0d344046d62f808c30bc5670cbb7dc478cca0a9798830d22f8f6ed27c76923c6
I 2014-05-20 05:18:31.754
feedback token_hex: 2230c2421e3b83cd6b16a69c6ba528230b11d29183b0bfb73b159816237b17ce
I 2014-05-20 05:18:31.754
feedback token_hex: 349c54d18bb1ee014dc84f7b7b60c4a2eef1b9d3cf51c12daab93261d5e09e7c
I 2014-05-20 05:18:31.754
feedback token_hex: 3980924c6cd4e752f2a02b8d28f7ce11d7a3eba5f41628166733cda4e621bfcf
I 2014-05-20 05:18:31.755
feedback token_hex: 6bd80feb5158a8f92537955c93acd1661242c007dcffebe55e77bb38cafef0ba
I 2014-05-20 05:18:31.755
feedback token_hex: b96e27adab644f0a18e8f4dfe19786aab82b69e1ef46c580b887e6779964c55f
I 2014-05-20 05:18:31.755
feedback token_hex: e5ee1848342d2e4789cfa07baae3ac754785d78ccb50dc5b5f10044053843115
I 2014-05-20 05:18:31.755
feedback token_hex: f339e53e44efa03996dffc24b5c9419609018fd8dd5d1953230a4bd8c5cabc78
I 2014-05-20 05:18:31.760
feedback fail_count: 9
Source: (StackOverflow)
I am using PyAPNs to send notifications to iOS devices. I am often sending groups of notifications at once. If any of the tokens is bad for any reason, the process will stop. As a result I am using the enhanced setup and the following method:
apns.gateway_server.register_response_listener
I use this to track which token was the problem and then I pick up from there sending the rest. The issue is that when sending the only way to trap these errors is to use a sleep timer between token sends. For example:
for x in self.retryAPNList:
apns.gateway_server.send_notification(x, payload, identifier = token)
time.sleep(0.5)
If I don't use a sleep timer no errors are caught and thus my entire APN list is not sent to as the process stops when there is a bad token. However, this sleep timer is somewhat arbitrary. Sometimes the .5 seconds is enough while other times I have had to set it to 1. In no case has it worked without some sleep delay being added. Doing this slows down web calls and it feels less than bullet proof to enter random sleep times.
Any suggestions for how this can work without a delay between APN calls or is there a best practice for the delay needed?
Adding more code due to the request made below. Here are 3 methods inside of a class that I use to control this:
class PushAdmin(webapp2.RequestHandler):
retryAPNList=[]
channelID=""
channelName = ""
userName=""
apns = APNs(use_sandbox=True,cert_file="mycert.pem", key_file="mykey.pem", enhanced=True)
def devChannelPush(self,channel,name,sendAlerts):
ucs = UsedChannelStore()
pus = PushUpdateStore()
channelName = ""
refreshApnList = pus.getAPN(channel)
if sendAlerts:
alertApnList,channelName = ucs.getAPN(channel)
if not alertApnList: alertApnList=[]
if not refreshApnList: refreshApnList=[]
pushApnList = list(set(alertApnList+refreshApnList))
elif refreshApnList:
pushApnList = refreshApnList
else:
pushApnList = []
self.retryAPNList = pushApnList
self.channelID = channel
self.channelName = channelName
self.userName = name
self.retryAPNPush()
def retryAPNPush(self):
token = -1
payload = Payload(alert="A message from " +self.userName+ " posted to "+self.channelName, sound="default", badge=1, custom={"channel":self.channelID})
if len(self.retryAPNList)>0:
token +=1
for x in self.retryAPNList:
self.apns.gateway_server.send_notification(x, payload, identifier = token)
time.sleep(0.5)
Below is the calling class (abbreviate to reduce non-related items):
class ChannelStore(ndb.Model):
def writeMessage(self,ID,name,message,imageKey,fileKey):
notify = PushAdmin()
notify.devChannelPush(ID,name,True)
Below is the slight change I made to the placement of the sleep timer that seems to have resolved the issue. I am, however, still concerned for whether the time given will be the right amount in all circumstances.
def retryAPNPush(self):
identifier = 1
token = -1
payload = Payload(alert="A message from " +self.userName+ " posted to "+self.channelName, sound="default", badge=1, custom={"channel":self.channelID})
if len(self.retryAPNList)>0:
token +=1
for x in self.retryAPNList:
self.apns.gateway_server.send_notification(x, payload, identifier = token)
time.sleep(0.5)
Resolution:
As noted in the comments at bottom, the resolution to this problem was to move the following statement to the module level outside the class. By doing this there is no need for any sleep statements.
apns = APNs(use_sandbox=True,cert_file="mycert.pem", key_file="mykey.pem", enhanced=True)
Source: (StackOverflow)
I've been trying to access the APNS in order to send push notifications to an iOS app I develop. I have created a development and production certificate requests and signed them on the Apple dev center:
My certificates
For each certificate (production and development), I have three files:
aps_development.cer: A
.cer` file I have downloaded from Apple after signing the certificate request.
dev.pem
: A .pem
public key file from the OSX keychain.
dev.p12
: A .p12
private key file from the OSX keychain, with password (and dev_nopass.p12
: A .p12
private key file from the OSX keychain, with password).
What I've tried
- Cloned and installed PyAPNs form github (The PyPi version seems a bit old and causes import errors.).
- Adapted the example code to:
from apns import APNs, Frame, Payload
p12 = '/path/to/dev.p12'
p12_nopass = '/path/to/dev_nopass.p12'
pem = '/path/to/dev.pem'
cer = '/path/to/aps_development.cer'
apns = APNs(use_sandbox=True,
cert_file=pem,
key_file=p12_nopass)
# Send a notification
# Dummy token, but it does not cause the error (SSL fails before token check)
token_hex = 'b5bb9d8014a0f9b1d61e21e796d78dccdf1352f23cd32812f4850b87'
payload = Payload(alert="Hello World!", sound="default", badge=1)
apns.gateway_server.send_notification(token_hex, payload)
Result:
Traceback (most recent call last):
File "apple_push_driver.py", line 18, in <module>
apns.gateway_server.send_notification(token_hex, payload)
File "/path_to/site-packages/apns.py", line 381, in send_notification
self.write(self._get_notification(token_hex, payload))
File "/path_to/apns.py", line 174, in write
return self._connection().write(string)
File "/path_to/apns.py", line 167, in _connection
self._connect()
File "/path_to/apns.py", line 151, in _connect
self._ssl = wrap_socket(self._socket, self.key_file, self.cert_file)
File "/path_to/ssl.py", line 387, in wrap_socket
ciphers=ciphers)
File "/path_to/ssl.py", line 141, in __init__
ciphers)
ssl.SSLError: [Errno 336265225] _ssl.c:351: error:140B0009:SSL routines:SSL_CTX_use_PrivateKey_file:PEM lib
I tried both p12
and p12_nopass
, production and dev, and got the same results.
Any idea why I can't make SSL connection in order to send a Push notification via PyAPNs?
Source: (StackOverflow)
Good day people!
I started having this problem last month when the registered devices on my app stopped receiving push notifications
At first I realized that it was because Apple dropped SSL v3 support so I performed a git pull of the pyapns update.
Push messages started sending properly again, but are not being received. The push server logs are returning few malformed payload errors, but the occurrence of a successfully sent notification is very low, and sometimes being sent in batches.
After rebuilding the push certificate, push messages started being sent on time, and successfully, but this only lasted for a few hours.
As of now, a direct push notification test request to my server is not even being delivered to test devices properly, which was not the case a few hours back.
This is my setup:
All push messages are queued via Resque.
Actual server pushing the payload is 'pyapns.'
Push server OS is Ubuntu 14.04.1 LTS.
Upon checking logs on my service, Resque, and pyapns, I was very sure that I am successfully sending the payload to the Apple push servers. There are no malformed payload responses from APNS, but still, no messages.
Push certificate is recently updated and is being used by server running pyapns.
A quick peek at the server shows that a copy of Entrust CA Root Certificate is installed (as per requirement for TLS connections, specified by Apple).
Can anyone help me in this problem? Thank you!
Source: (StackOverflow)
I'm sending 1-10 notifications at a time, sometimes one of the tokens fail, I'm guessing they fail when a user deletes the app (or other similar reasons), do I need to retry the remaining tokens/notifications for that batch? (I receive a "failed token" warning later on from PyAPNS)
The tokens are all valid tokens, it's stated that malformed tokens require a retry of the remaining push notifications, however I'm wondering whether I need to retry too with non-malformed tokens, it would be pretty stupid If I need to retry in the above scenario, people delete apps all the time, I'm guessing a high percentage of batches would require a retry
(There are many questions related to this issue, however I wasn't able to find a definite answer, from my trials I'm guessing I already have my answer, It seems the notifications aren't delivered in the above scenario)
Source: (StackOverflow)
we are using PYAPNS to send push notifications from an Django application to iOS devices running an phonegap application.
The server code looks like this:
def sendNotificationAPNS(title, message, targets, eventid):
apns = APNs(use_sandbox=False, cert_file='/path/to/push_cert.pem', key_file='/path/to/push_cert.pem')
# Send a notification
for token_hex in targets:
payload = Payload(alert=message, sound="default", badge=1, custom={'eventid':str(eventid)})
apns.gateway_server.send_notification(token_hex, payload)
print('Notification to APNS send!')
return true
On the mobile application the code from Phonegap Push Plugin looks like this:
// handle APNS notifications for iOS
function onNotificationAPN(e) {
if (e.alert) {
console.log('APNS Notifiation recieved: ' + e.alert);
// showing an alert also requires the org.apache.cordova.dialogs plugin
navigator.notification.alert(e.alert);
}
if (e.sound) {
// playing a sound also requires the org.apache.cordova.media plugin
var snd = new Media(e.sound);
snd.play();
}
if (e.badge) {
pushNotification.setApplicationIconBadgeNumber(successHandler, e.badge);
}
//eventid
if(e.eventid) {
console.log('APNS eventid: ' + e.eventid);
}
//custom
if(e.custom) {
console.log('APNS eventid: ' + e.custom.eventid);
}
}
The problem is: I don't get anything for e.custom or e.eventid?!
What do I have to change to access the custom payload?
Thanks!
Source: (StackOverflow)
I’m sure this is obvious, but I’m missing it. I’ve installed PyAPNs via pip:
# pip install apns
Then when I try to use the “enhanced” flag in APNs, it’s not there.
# python
Python 2.7.6 (default, Nov 11 2013, 18:34:29)
[GCC 4.4.7 20120313 (Red Hat 4.4.7-3)] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> from apns import APNs
>>> server = APNs(use_sandbox=True, cert_file=“/mydir/apns-dev-cert.pem", key_file=“/mydir/apns-dev-key.pem", enhanced=True)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: __init__() got an unexpected keyword argument 'enhanced'
>>>
And sure enough the 1.1.2 version that pip installed doesn’t have that keyword. But I thought this was the latest released version in the PyAPNs repository https://github.com/djacobs/PyAPNs.
I want to use the ‘enhanced’ keyword, for error checking. Any ideas?
Source: (StackOverflow)