Cross posted on dev.to
Capturing the Device Tokens is necessary for push notification to work. A Device Token is nothing but an ID that uniquely identifies a combination of a device and an app. So if we have 2 apps running on the same device, they will have different Device Tokens. Likewise, if the same app runs on 2 different devices, they will also have different Device Tokens. This is obvious because Apple Push Notification service (APNs) must know where to correctly push the notifications to.
To receive the Device Token, an app must register with APNs. I copied the following code from honestly-I-do-not-remember-where
and paste in the AppDelegate
class:
It worked great. I never bothered to question why or how.
Then comes trouble. iOS 13 arrived. Customers who updated their devices to iOS 13 no longer receive push notifications.
My boss informed me. Then I found this post on App forum and from NSHipster. It then makes all senses to me because the following line is returned by the toString()
method in Xamarin C# NSData
(equivalently, in Swift, that’s the description field of the native Objective-C/ Swift NSData
structure):
<124686a5 556a72ca d808f572 00c323b9 3eff9285 92445590 3225757d b83997ba>
So after we call Replace
with the regex pattern[^0-9a-zA-Z]
as in line
DeviceToken = Regex.Replace(deviceToken.ToString(), "[^0-9a-zA-Z]+", "");
We end up with the Device Token (if you count, that’s 32 bytes)
124686a5556a72cad808f57200c323b93eff9285924455903225757db83997ba
The trouble is: in iOS 13, the description field of the NSData structure now contains something like
{length = 32, bytes = 0xd3d997af 967d1f43 b405374a 13394d2f … 28f10282 14af515f }
Important: The elliptic (…) in the line above is NOT from my abbreviation. It is the ACTUAL string returned by APNs.
So obviously if we call Replace
with the regex pattern[^0-9a-zA-Z]
as above, we end up with a wrong Device Token:
length32bytes0xd3d997af967d1f43b405374a13394d2f28f1028214af515f
It got the junk length32bytes0x
in the beginning, and it’s no longer 32 bytes in length.
The NSHipster article shows the fix in Swift:
A nice one-line in Swift. We can do equally well in C#:
Now let’s break this down:
- First we have to grab all the bytes in the device token by calling the
ToArray()
method on it. - Once we have the bytes which is an array of bytes or,
byte[]
, we call theLINQ Select
which applies an inner function that takes each byte and returns a zero-padded 2 digit Hex string. C# can do this nicely using the format specifierx2
. TheLINQ Select
function returns anIEnumerable<string>
, so it’s easy to callToArray()
to get an array of string orstring[]
. - Now just call
Join()
method on an array of string and we end up with a concatenated string.