Correctly capture iOS 13 Device Token in Xamarin
Tags: xamarinforms, ios, xamarinappdevelopment, xamarin
Published: 2019-10-24 16:58:17

---
title: Correctly capture iOS 13 Device Token in Xamarin
published: true
description: 
tags: xamarin forms, iOS, xamarin app development
---

*Cross posted on [Medium](https://medium.com/@kevinle/correctly-capture-ios-13-device-token-in-xamarin-3d0fa390b71b)*

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:

``` csharp
[Register("AppDelegate")]
public partial class AppDelegate : global::Xamarin.Forms.Platform.iOS.FormsApplicationDelegate
{
    public string DeviceToken { get; set; }
    
    public override bool FinishedLaunching(UIApplication app, NSDictionary options)
    {
        ...
        LoadApplication(new App());
        ...
        return base.FinishedLaunching(app, options);
    }

    public override void RegisteredForRemoteNotifications(UIApplication application, NSData deviceToken)
    {
        DeviceToken = Regex.Replace(deviceToken.ToString(), "[^0-9a-zA-Z]+", "");
        ...        
    }

    public override void ReceivedLocalNotification(UIApplication application, UILocalNotification notification)
    {
        ...
    }

    public override void ReceivedRemoteNotification(UIApplication application, NSDictionary userInfo)
    {
        ...
    }
}
```
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](https://forums.developer.apple.com/thread/117545) and from [NSHipster](https://nshipster.com/apns-device-tokens/). 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
``` csharp
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](https://nshipster.com/apns-device-tokens/) shows the fix in Swift:
```swift
let deviceTokenString = deviceToken.map { String(format: "%02x", $0) }.joined()
```
A nice one-line in Swift. We can do equally well in C#:
``` csharp
public override void RegisteredForRemoteNotifications(UIApplication application, NSData deviceToken)
{
    //DeviceToken = Regex.Replace(deviceToken.ToString(), "[^0-9a-zA-Z]+", "");
    //Replace the above line whick worked up to iOS12 with the code below:
    byte[] bytes = deviceToken.ToArray<byte>();
    string[] hexArray = bytes.Select(b => b.ToString("x2")).ToArray();
    DeviceToken = string.Join(string.Empty, hexArray);
}
```
Now let’s break this down:

1. First we have to grab all the bytes in the device token by calling the `ToArray()` method on it.

2. Once we have the bytes which is an array of bytes or, `byte[]`, we call `LINQ 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 specifier `x2`. The `LINQ Select` function returns an `IEnumerable<string>`, so it’s easy to call `ToArray()` to get an array of string or `string[]`.

3. Now just call `Join()` method on an array of string and we end up with a concatenated string.