I'm currently implementing push notifications from our backend server to our app (macOS Catalina & iOS - same code base), using Apple Push Notifications & the token based way of authentication (generating JWT from keyId, teamId, ... & signing it with the private key generated in the Apple developer console) to send pushes to APN service.
The problem I am facing is that I can successfully send "alert" notifications (status 200, with header apns-push-type: alert) and receive them on my iOS and MacOS device (the push notifications appear successfully in production and sandbox mode) but for some reason, "silent" pushes (with header apns-push-type: background) are only received on my iOS device (iPhone), but not on my Mac (didReceiveRemoteNotification(...) in AppDelegate is never called).
What I have done so far:
Made sure APN request header is correctly configured for silent push: apns-push-type: background
Made sure to have the correct APN topic header: apns-topic: my.bundle.id (this is different for sandbox/production)
Added the "semi-required" priority header: apns-priority: 5 (only when delivering background pushes)
Verified that the created JWT is valid and used in the APN auth header: authorization: mytoken (this must be the case, otherwise APN service would not respond with status 200)
Confirmed that my application has the correct entitlements & capabilities defined in Xcode (my reasoning: this must be the case, otherwise 'alert' push notifications would also not work)
Double checked that push notifications for the Mac app are allowed/enabled (checked system settings)
Made sure the device token im sending the push to is actually from the device intended to receive the push (e.g. my MacBook)
Checked that the private key I'm using to sign the JWT has the APN capability
Tested while the Mac app is running but not in focus & also when in focus
The APNs requests I am performing to send the push are:
Sandbox: POST https://api.sandbox.push.apple.com/3/device/{deviceToken}
Production: POST https://api.push.apple.com/3/device/{deviceToken}
The payload (JSON) I'm sending to APN service in the request body looks as follows:
{
"aps": {
"content-available": 1 # defines push as "silent"
},
"data": { #some key-value pairs here }
}
In any case (both production & sandbox, both with the device token of iOS and macOS), my request to APN returns with a status code 200. My MacBook is running Catalina 10.15.3. What am I possibly doing wrong here or is that something that is simply not supported for Catalyst apps?
Wow doozy question. I'm reasonably familiar with APNs headaches but something popped out at me from the latest docs:
Additionally, the notification’s POST request should contain the
apns-push-type header field with a value of background, and the
apns-priority field with a value of 5. The APNs server requires the
apns-push-type field when sending push notifications to Apple Watch,
and recommends it for all platforms. For more information, see Create
and Send a POST Request to APNs.
Does the priority 5 thing make a difference?
Also my usually attempt to fix these problems is to test in an archive as opposed to an Xcode build. AFAIK the prod push server can only send to App Store, ad-hoc, enterprise, or testflight builds, so if you're just building from Xcode I don't think you'd get any push notifications with production apns.
Related
I'm a little confused on the difference between using Certificate based connections to the APN service verse the Device Token method.
My understanding is, when sending a push notification to an iOS device, my options are either:
A) I could have registered the APN certificate with the Application bundle, and my provider can send it to the APN service to verify I'm allowed to send notifications to devices with that Application bundle.
B) My provider could be sent a "Device Token" via the downloaded application on the device over HTTP, and when I send that Device Token to the APN service, it will know the user has allowed me to send those notifications.
My questions are:
1) If I were to use method A, how could I target certain devices to send the notifications to if I don't have a device token?
2) If I use method B, where would I receive the device token to send silent notifications if my user "Doesn't Allow" push notifications? Will
func application(_ application: UIApplication,
didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data)
Still run and allow me to pull the deviceToken out, so I can send it via HTTP to my provider?
I've adopted a piece of software that used Certificate based Push Notifications (I think) and was unsure of how it was differentiating who to send notifications to with any deviceTokens. Once I rebuilt and redownloaded the application on my iOS device the notifications stopped working, so I assumed the certificate was no longer associated with the build.
I'm also curious as to how someone can debug notifications on a physical device. I've tried the Console app on Mac and have had little luck seeing any print statements I put in the code.
I'm still very new to iOS programming so please correct me if any of my understanding of the APN Service is wrong! Thank you very much in advance!
There are a few things to understand:
Your Company/Team's Certificate
Your App/Bundle's Provisioning profile
Your Client/User's Device Token
Your BackEnd Server
1 & 2 are bundled in your app when you upload it to App Store.
You get 3 when you ask user the permission for sending PN. [For this 2 should have the capability for remote push notifications enabled]
You give 1 & 3 to 4. [1 for establishing connection with APNS server, 3 to send PN to a specific iOS device]
So, to answer your question: You need both A & B.
For better understanding how APNS works I am attaching a reference diagram that would help to clear your concept for the same.
** If I were to use method A, how could I target certain devices to send the notifications to if I don't have a device token?**
Without Device token, you won't be able to target the iPhones or
idevices on which you want to send the push notification
if my user "Doesn't Allow" push notifications -> then you won't be able to generate PUSH DEVICE TOKEN hence in this scenario you won't be able to receive any push notification.
I am trying to determine what I need to do with respect to the combination of APNS endpoint (e.g., development or production), Xcode, and Apple Push certificates in order to test push notifications while in development. I feel like I've tried every possible combination, but I must be missing something ...
Background
When using Apple's HTTP/2 APNS endpoints from my "dispatch" server, my app/device does not receive pushes, and I receive a BadDeviceToken response from Apple.
Using the exact same .p12 certificate and deviceToken with the Pusher macOS testing app (which uses legacy APNS endpoints), the pushes successfully deliver.
To complicate this further ...
When using Apple's HTTP/2 APNS endpoints from my "dispatch" server for PassKit pushes, my pass/device does receive pushes.
So ...
Point #3 tells me that my "dispatch" server must be configured properly, because pushes to the Apple Wallet pass cause a response (e.g., I can see follow-on requests to my server's endpoints from the Wallet / the pass).
That said, Apple Wallet is a "Production" app. I suspect that, because my app (from Point #1) is non-production/development, something is different.
.
Question
Has anyone been able to successfully receive -- in Xcode -- push notifications sent to Apple's api.development.push.apple.com endpoint? Can you outline the steps you performed (which certificate from developers.apple.com, etc.)? Thank you!
You should be able to send development push from server:
You need to connect to api.development.push.apple.com:443 instead of api.push.apple.com:443. You can use production certificate for both.
Sending push to production server can not work with development builds - only with a build that is exported with AppStore configuration, but you can't debug those (at least not with Xcode)
If you need to check whether the production endpoint works, you can use testflight
Set development certificate from apple developer portal.
Use either production/test server for communicating with APNS.
Edit your target scheme as follows :
This will ensure that when push notification arrives, control will itself fall in the code. It might seem confusing. But here it is how it works :
- Install app on device and stop the Run process from Xcode.
- Place a breakpoint on didReceiveRemoteNotification.
- Send a push notification to device.
- Xcode will itself start the app and control will go to the above function.
I've got a problem with the integration of Apple push notifications for Apple Wallet with my web service.
What works:
Endpoints (as described in https://developer.apple.com/library/content/documentation/PassKit/Reference/PassKit_WebService/WebService.html);
Passes are valid, they work with an iPhone, registering, manual update, unregistering, etc. seem to work, no errors from the logs of my server;
The response from "Getting the Latest Version of a Pass" endpoint has the "Last-Modified" header.
Then I update a pass on the server. When I try to send a push notification nothing happens.
I did some debug, APN Server (https://api.push.apple.com:443) returns code 200 with an empty body and non-empty "apns-id" header. This looks OK (https://developer.apple.com/library/content/documentation/NetworkingInternet/Conceptual/RemoteNotificationsPG/CommunicatingwithAPNs.html).
BUT! There are neither notifications nor an updated pass in Wallet on my iPhone.
When I do manual update, the pass updates and an update notification shows.
I cannot get what I did wrong...
P.S. I use https://github.com/mdigger/apns for making requests to the APNS.
When connecting to APNS for Wallet updates, you need to connect with the same Pass Type ID Certificate that was used to sign the pass.
See the section:
Your Server Sends a Push Notification When Something Changes
in the wallet documentation.
Your server sends the following pieces of information:
The pass type identifier (in the certificate)
The push token (in the communication to the Apple Push Notification service)
I want to send push notification from a iOS device to another iOS device without using backend server. Is it possible for an iOS device to act like a server and send push notification to APNs server?.
Thanks in advance.
Theoretically you can send Apple Push Notifications from a device directly to another device. All you need are the push certificate of the app, the device token of the device you are sending the notification to, and code that establishes a secure TLS connection to the APNS servers.
However, there are several practical problems that make the use of a server almost mandatory :
You need a single place where all the device tokens of all the devices that installed your app will be sent to and persisted in. The best such place would be a server. Without a server, how would device A send its device token to other devices that want to send it push notifications?
Apple require that you keep connections with the APNS server open for as long as possible and use the same connection for sending many notifications. If you open a connection to APNS server on your device, it will probably be short lived (since devices switch networks frequently, and don't stay connected to the internet all the time). Therefore, if you try to send many notifications frequently, and each time use a new connection to APNS, you will probably be banned (since Apple would treat this as DDoS attack).
If you store the push certificate in each device that installs your app (to allow it to send push notifications to other devices directly), aside from the security issue of storing the certificate in many places, you'll have to publish a new version of your app each time the push certificate expires (once a year), and push notifications would stop working for users who don't upgrade to the new version.
Try NWPusher.
It has an iOS framework for sending pushes and has an iOS demo application that sends push notifications from iOS to iOS.
You also need to consider Server costs (other than maintenance and development time if you code your own server).
By sending the push directly from the app device:
- you obtain a much better scalability (since you don't have to centralize everything on your server)
- you don't have to pay for server cost or other service's cost
You can use for iOS:
- https://github.com/noodlewerk/NWPusher Pusher
And for Android:
- Send push notification GCM by java
By that I mean: If you have an app running on both platform, can you be sure that a given token on iOS isn't attributed to Mac? I'm pretty sure this isn't something we can "know" (Apple internal) and I shouldn't assume it, but I'm really curious what happens if you (by mistake) send an "iOS" Push (intended for your iOS App) to a Mac token. Could it reach another iOS user?! I guess potentially...
Imagine the following case (simplified):
You know you have to send a push to the token "foo" to your Mac app.
You mistakenly send a push to "foo" on your iOS app.
I hope it would land in void land / you'd get an error back from APNS but I guess it might also land on a "random" user of your iOS app, which is not the user intended (on the Mac app)
Sending a push notification requires an SSL certificate from Apple that is bound to your iOS or Mac application. Therefore it can not happen that a notification for an iOS app is sent to a Mac app.
From the following quote it doesn't seem possible that the same device token will be assigned to both an iOS device and a Mac (or to two iOS devices or to two Macs), since device tokens contain device IDs, and device IDs should be unique (otherwise they wouldn't be very useful IDs).
Every notification that a provider sends to APNs for delivery to a device must be accompanied by the device token it obtained from an application on that device. APNs decrypts the token using the token key, thereby ensuring that the notification is valid. It then uses the device ID contained in the device token to determine the destination device for the notification.
(Source)