Cannot get PushKit to register for Watch app

Here is the code:

let pushRegistry = PKPushRegistry(queue: .main)

func applicationDidFinishLaunching() {
    pushRegistry.delegate = self
    pushRegistry.desiredPushTypes = [.complication]
}

func pushRegistry(_ registry: PKPushRegistry, didUpdate pushCredentials: PKPushCredentials, for type: PKPushType) {
    let token = pushCredentials.token.reduce("") { $0 + String(format: "%02x", $1) }
    print(token)
}

The watch extension has push notifications capability, and the delegate is never called with the pushCredentials.

The most common reasons for not being able to receive a token are either a project misconfiguration, or a network issue.

In your project, please check that the "APS Environment" entitlement (typically in the file <your app name>.entitlements) exists for the watch extension, and is set to either development or production

Also make sure that the provisioning profile you are using to build your app has Push Notifications enabled, along with your project itself having this enabled.

For push notifications to work, your device needs a persistent, non-proxied connection to APNs, and your network needs to be configured to allow access to the APNs ports and IP address ranges.

You can read troubleshooting details at this document: https://support.apple.com/en-us/HT203609

We see this problem occur mostly due to a misconfigured local network or blocking the APNs server addresses and ports.

In this context, a connection issue means that the device is not able to reach the APNs servers/ports, and/or not reachable by the APNs servers directly and reliably.

Fixing either or both of these will solve the problems for majority of developers.

Other thing to try would be to restart the watch, which will cause the device to try to reestablish its persistent connection to APNs and can fix the issue.

Thank you. Here is some more info:

I was able to get the token using a different app, with similar configuration. The main difference is that my app is flutter based, and the other one is native. Could this be the cause? The watch app needs to talk to the Phone app, in order to connect to the servers? (Flutter app do not run in debug mode)

On its own, I don't think Flutter would be an issue, however, it might have made it easier to miss a critical detail. Did you include "voip" in UIBackgroundModes? Missing that value will also cause exactly what you're seeing.

I do have one word of caution here. One issue I've seen with voip apps implemented with 3rd party development architectures is that you can end up putting substantial development effort into a particular implementation without realizing that the design choices you were making were simply never going to work.

For example, on iOS I don't think it's actually possible to build a functioning voip app that uses WebKit's WebRTC support for it's networking. It LOOKS like it's possible on in terms of the "basic" API and you can even get it working pretty well while the app is in the foreground. However, once you try to support things background calling and app launching things fall apart because of issues like:

  1. CallKit relies on the calling App audio session, while WKWebView routes audio through it's helper processes.

  2. WKWebView is built as a view class and wasn't really designed to "run in the background", so it doesn't provide the level of control a voip app would need to manage it's helper processes in the background.

In both cases, these aren't bugs or "problems" with either API, they're simply the way the API was designed to work. The key point here is to make sure "everything" (particularly around background operation) before you put to much work into things.


-Kevin Elliott
DTS Engineer, CoreOS/Hardware

Do I need "voip" in background mode? I'm using pushKit to update complications, this is not a voip app.

Also on my test app where I get the credentials, I do not have any background modes at all.

Do I need "voip" in background mode? I'm using pushKit to update complications, this is not a voip app.

Sorry about that. voip apps are the heaviest users of PushKit, so I assumed that without reading more closely. So, two things to look at:

  1. Your notification must be sent with the topic that's exactly:
<app bundle id>.complication

...as anything else will fail exactly the way you're seeing.

  1. The system is pretty good about logging APNS activity, particularly when something blocks delivery the push itself, so you should be able to determine what's going on from there. A few things that could be helpful with that:
  • As much as possible, do your testing on a dedicated device with "nothing" else installed. The biggest problem with console log analysis is that the log volume is extremely high, so anything you can do to reduce the volume is a good thing. In addition, it also helps when log redacting means that you're getting incomplete data. For example, APNS redacts most bundle IDS, so you'll end up seeing things like "...topic: <redacted>.complication...". That's much less of problem if you know your app is the only app on the device that could receive a complication push.

  • On the redaction side, you can try installing the "FaceTime" profile from our Bug Reporting - Profile & Logs page. watchOS doesn't have an APNS profile like iOS does, but the FaceTime profile is actually for debugging CallKit/PushKit and, as part of that, it also removes redaction on APNS. I don't know if that will work on watchOS, but it might be worth trying.

  • The instructions for that profile also describe how to collect a sysdiagnose and I would STRONGLY recommend collecting and then analyzing that data instead of trying to work out of the "live" console log. Once you've got the sysdiagnose file to your mac and unzipped it, you'll find a file named "system_logs.logarchive" (along with a bunch of other "stuff") inside it. You can open that file with Console.app and now you don't need to worry about "something" going wrong and you losing all the data you were trying to undertand.


-Kevin Elliott
DTS Engineer, CoreOS/Hardware

As mentioned in my question, the issue is not receiving a notification, but getting the token.

If my question was not clear, here is a clarification - I basically do not get push credentials delegate being called on watch app start, in order to extract the token. This only happens on some of my apps, also when testing with the same bundle ids. Thank you!

Apologies, it does look like we have the lines crossed with your question and our answers. Thank you for clarifying your original question.

Then, I suppose we are back to the first answer in this thread. As you have clarified that another app can get the token, and your cannot, that eliminates the connection problem.

Which leaves us with a project configuration issue. Like I said, the most common cause would be a missing APS Environment entitlement correctly set to "development" or "production".

As your is a Flutter app, we will not be able to assist with that might be wrong with the setup. Perhaps another developer who has seen this scenario can jump in with a solution here. Otherwise this is an issue you should investigate on the Flutter side, and you may want to consult their support resources as necessary.

Does pushKit functionality require stand alone watch apps?

Cannot get PushKit to register for Watch app
 
 
Q