Getting the device token string needed for push notifications
How to convert the Data handed to you by didRegisterForRemoteNotificationsWithDeviceToken into a String, and what changed about description in iOS 13.
- Published
This article is also published elsewhere. https://iganin.hatenablog.com/entry/2019/10/14/173102
Originally written in Japanese. This is a translation of the same piece.
Introduction
To send push notifications from a server, the server needs to hold the device token string and talk to APNs
(Apple Push Notification service) with that token. The device token arrives via the
application(_:didRegisterForRemoteNotificationsWithDeviceToken:) method after you call
registerForRemoteNotifications().
The token you receive is of type Data, though, so in most cases you need to convert it to a String
before sending it to the server. This article covers that conversion, along with the change to the
description method in iOS 13 and later.
Environment
- iOS 13.1.2
- Xcode 11.1.0
What changed in iOS 13
First, the change to what description returns in iOS 13 and later.
Before iOS 13, (deviceToken as NSData).description gave you a string like
<1xxxxxx5 55xxxxca xxxxx572 00c3xxxx xxxxxxx5 92xxxx90 xxxxxxx7d bxxxxxxa>. Depending on the service, you
might have stripped the <> and the spaces with a method like the one below before sending it, or sent it
to the server as-is.
// Example of a method that strips the <> and the spaces
let token = ((deviceToken as NSData).description.trimmingCharacters(in: CharacterSet(charactersIn: "<>")) as NSString).replacingOccurrences(of: " ", with: "")
In iOS 13 and later, however, NSData’s description changed to return something like
{length=32,bytes=0x3dd8xxxxxxxx0be290cxxxxxxxxxx2e0...7xxxxxxxxxxxx0a}. The format is very different, and
because the string portion now includes a ... elision, the device token can no longer be recovered from
it.
You can see a number of bug-fix releases on the App Store that look like they were caused by exactly this.
How to handle it
If you simply want the string portion, this method from @mono0926 is nice and simple. It gives you a string
like 12xxxx6a5xxxxxxxcad80xxxxxxxxxx3b93exxxxxx9xxxxxxx32xxxxxxb83967be.
let token = deviceToken.map { String(format: "%.2hhx", $0) }.joined()
That said, if you have been sending NSData’s description return value directly, circumstances may
require you to keep sending the device token string with the <> and spaces included. In that case you can
reproduce the same string as NSData’s description like this.
Taking the original string as the reference, this converts the Data into a string in 4-byte groups.
Adding <> at the end reproduces what NSData’s description returned before iOS 13. Accounting for
capacity when creating the array would probably make it more efficient, but I judged execution speed not to
be a concern here and left it as-is. Use it as String(deviceToken: deviceToken) to get the string.
extension String {
/// An initialiser that builds a device token from Data.
/// Produces the token needed to run remote notifications.
///
/// - Parameter deviceToken: the device token string built from Data
/// e.g. <70a7ff15 cxxx3c44 bd0xxx2c f75xxxxb dxxxc23f ff14xxxe 6xxxx3f2 b6xxxxxb>
init(deviceToken: Data) {
let groupingUnit: Int = 4 // build the string in groups of 4 bytes
let groupCount: Int = 8 // build 8 groups of 4 bytes
var deviceTokenStrings = [String](repeating: "", count: groupCount)
deviceToken.enumerated().forEach { deviceTokenStrings[Int($0) / groupingUnit] += String(format: "%.2hhx", $1) }
self = "<" + deviceTokenStrings.joined(separator: " ") + ">" // wrap it in < >
}
}
Summary
That covers getting the device token string needed to send push notifications, both with and without the
<> and spaces. If you were using NSData’s description and had to deal with this in iOS 13, I hope it
helps.
References
- Apple Document - Registering Your App with APNs
- “Register Your App and Retrieve Your App’s Device Token” describes the device token registration steps.
- Converting a push notification device token (Data) to a hex string in Swift (Japanese)
- @mono0926’s article on getting the device token string. Very helpful.
- Apple Developer Forum - iOS13 PKPushCredentials broken
- Covers the change to what
NSData’sdescriptionreturns in iOS 13.
- Covers the change to what