MAUI Push Notifications using Azure Notification Hub for iOS

ios push notification

After a few weeks of fighting with the Push Notifications in NET8 MAUI using Azure Notification Hub for iOS and it wasn’t working, I finally found the way to do it. In this post, I will explain every step and how to implement the push notification, jumping from the Apple developer website, Azure portal and the code in C#.

So, I show you everything I discovered without using external plugin but only what MAUI offers and HttpClient. I split this topic in a few posts:

Now, it is time to start the journey and discover how to do it.

Configure push notification in the Apple Developer portal

First, I have to create the certificates from the Apple Developer portal specifically for the application. The prerequisites for that are:

  • An active Apple Developer account.
  • A Mac running Xcode, along with a valid developer certificate, is installed into your Keychain.
  • An iPhone or iPad running iOS version 10 or later.
  • Your physical device registered in the Apple Portal and associated with your certificate.

Generate the certificate-signing request file

The Apple Push Notification Service (APNS) uses certificates to authenticate your push notifications. Follow these instructions to create the necessary push certificate to send and receive notifications. For more information on these concepts, see the official Apple Push Notification Service documentation.

Generate the Certificate Signing Request (CSR) file, which Apple uses to generate a signed push certificate:

  1. On your Mac, run the Keychain Access tool. It can be opened from the Utilities folder or the Other folder on the Launchpad.
  2. The application is asking you what I want to open. You should see the following window. Select Open Keychain access.
KeyChain request - MAUI Push Notifications using Azure Notification Hub for iOS
Keychain request
  1. Select Keychain Access, expand Certificate Assistant, and then select Request a Certificate from a Certificate Authority.
Request a Certificate From a Certificate Authority from my Mac - MAUI Push Notifications using Azure Notification Hub for iOS
Request a Certificate From a Certificate Authority from my Mac
  1. Select your User Email Address, enter your Common Name value, make sure that you specify Saved to disk, and then select Continue. Leave CA Email Address blank as it isn’t required.
KeyChain Certificate Assistant on my Mac
Keychain Certificate Assistant on my Mac
  1. Enter a name for the CSR file in Save As, select the location in Where, and then select Save. This action saves the CSR file in the selected location. The default location is Desktop. Remember the location chosen for the file.
KeyChain Certificate Assistant saves the request
Keychain Certificate Assistant saves the request

Next, register your app with Apple, enable push notifications, and upload the exported CSR to create a push certificate.

Register your app for push notifications

To send push notifications to an iOS app, register your application with Apple, and also register for push notifications.

  1. If you haven’t already registered your app, browse to the iOS Provisioning Portal at the Apple Developer Center. Sign in to the portal with your Apple ID, and select Identifiers. Then select + to register a new app.
Apple Developer website: Certificates, Identifiers & Profile
Apple Developer website: Certificates, Identifiers & Profile
  1. On the Register a New Identifier screen, select the App IDs radio button. Then select Continue.
Apple Developer website: Certificates, Identifiers & Profile
Apple Developer website: Certificates, Identifiers & Profile
  1. Update the following three values for your new app, and then select Continue:
    • Description: Type a descriptive name for your app.
    • Bundle ID: Enter a Bundle ID of the form Organization Identifier.Product Name as mentioned in the App Distribution Guide. The Organization Identifier and Product Name values must match the organization identifier and product name you use when you create your Xcode project. In the following screenshot, the NotificationHubs value is used as an organization identifier and the GetStarted value is used as the product name. Make sure the Bundle Identifier value matches the value in your Xcode project so that Xcode uses the correct publishing profile.Register app ID
    • Push Notifications: Check the Push Notifications option in the Capabilities section.Register new app IDThis action generates your App ID and requests that you confirm the information. Select Continue, then select Register to confirm the new App ID.Confirm new App IDAfter you select Register, you see the new App ID as a line item on the Certificates, Identifiers & Profiles page.
  2. In the Certificates, Identifiers & Profiles page, under Identifiers, locate the App ID line item that you just created, and select its row to display the Edit your App ID Configuration screen.

Create a certificate for Notification Hubs

With the release of iOS 13, you can only receive silent notifications using token based authentication. If you are using certificate-based authentication for your APNS credentials, you must switch to using token-based authentication.

A certificate is required to enable the notification hub to work with APNS. This can be done in one of two ways:

  • Create a .p12 file that can be uploaded directly to Notification Hubs.
  • Create a .p8 file that can be used for token-based authentication (the newer approach).

The second option has a number of benefits compared to using certificates, as documented in Token-based (HTTP/2) authentication for APNS. However, steps are provided for both approaches.

Option 1: Create a .p12 push certificate that can be uploaded directly to Notification Hubs

  1. Scroll down to the checked Push Notifications option, and then select Configure to create the certificate.
  1. The Apple Push Notification service SSL Certificates window appears. Select the Create Certificate button in the Development SSL Certificate section. The Create a new Certificate screen is displayed. Follow the same process for the Production certificate.
  1. Select Choose File, browse to the location at which you saved the CSR file from the first task, and then double-click the certificate name to load it. Then select Continue.
  2. After the portal creates the certificate, select the Download button. Save the certificate, and remember the location to which it’s saved. The certificate is downloaded and saved in your Downloads folder. By default, the downloaded development certificate is named aps_development.cer.
  1. Double-click the downloaded push certificate aps_development.cer. This action installs the new certificate in the Keychain, as shown in the following image
  1. In Keychain Access, right-click the new push certificate that you created in the Certificates category. Select Export, name the file, select the .p12 format, and then select Save.

You can choose to protect the certificate with a password, but this is optional. Click OK if you want to bypass password creation. Make a note of the file name and location of the exported .p12 certificate. They are used to enable authentication with APNS.

Option 2: Create a .p8 certificate that can be used for token-based authentication

  1. Make note of the following details:
    • App ID Prefix (this is a Team ID)
    • Bundle ID
  2. Back in Certificates, Identifiers & Profiles, click Keys. If you already have a key configured for APNS, you can re-use the .p8 certificate that you downloaded right after it was created. If so, you can ignore steps 3 through 5.
  3. Click the + button (or the Create a key button) to create a new key.
  4. Provide a suitable Key Name value, check the Apple Push Notifications service (APNS) option, and then click Continue, followed by Register on the next screen.
  5. Click Download and then move the .p8 file (prefixed with AuthKey_) to a secure local directory, then click DoneImportant: Be sure to keep your .p8 file in a secure place (and save a backup). After downloading your key, it cannot be re-downloaded; the server copy is removed.
  6. On Keys, click on the key that you just created (or an existing key if you have chosen to use that instead).
  7. Make note of the Key ID value.
  8. Open your .p8 certificate in a suitable application of your choice, such as Visual Studio Code, then make note of the key value. This is the value between —–BEGIN PRIVATE KEY—– and —–END PRIVATE KEY—–
-----BEGIN PRIVATE KEY-----
<key_value>
-----END PRIVATE KEY-----

At the end of these steps you should have the following information for use later in Configure your notification hub with APNS information:

  • Team ID (see step 1)
  • Bundle ID (see step 1)
  • Key ID (see step 7)
  • Token value (the .p8 key value, see step 8)

Configure the notification hub with APNS information

Under Notification Services, select Apple (APNS), then follow the appropriate steps based on the approach you chose previously in the Creating a Certificate for Notification Hubs section.

Option 1: Use a .p12 push certificate

  • Select Certificate.
  • Select the file icon.
  • Select the .p12 file that you exported earlier, and then select Open.
  • If required, specify the correct password.
  • Select Sandbox mode.
  • Save

Option 2: Use token-based authentication

  1. Select Token.
  2. Enter the following values that you acquired earlier:
    • Key ID
    • Bundle ID
    • Team ID
    • Token
  3. Choose Sandbox
  4. Select Save.

You’ve now configured your notification hub with APNS. You also have the connection strings needed to register your app and send push notifications.

Create a provisioning profile

  1. Return to the iOS Provisioning Portal, select Certificates, Identifiers & Profiles, select Profiles from the left menu, and then select + to create a new profile. The Register a New Provisioning Profile screen appears.
  2. Select iOS App Development under Development as the provisioning profile type, and then select Continue.
  1. Next, select the app ID you created from the App ID drop-down list, then select Continue.
  1. In the Select certificates window, select the development certificate that you use for code signing, and select Continue. This certificate isn’t the push certificate you created. If one does not exist, you must create it. If a certificate does exist, skip to the next step. To create a development certificate if one does not exist:
    1. If you see No Certificates are available, select Create Certificate.
    2. In the Software section, select Apple Development. Then select Continue.
    3. In the Create a New Certificate screen, select Choose File.
    4. Browse to the Certificate Signing Request certificate you created earlier, select it, and then select Open.
    5. Select Continue.
    6. Download the development certificate, and remember the location in which it’s saved.
  2. Return to the Certificates, Identifiers & Profiles page, select Profiles from the left menu, and then select + to create a new profile. The Register a New Provisioning Profile screen appears.
  3. In the Select certificates window, select the development certificate that you just created. Then select Continue.
  4. Next, select the devices to use for testing, and select Continue.
  5. Finally, choose a name for the profile in Provisioning Profile Name, then select Generate.
  1. When the new provisioning profile is created, select Download. Remember the location in which it’s saved.
  2. Browse to the location of the provisioning profile, and then double-click it to install it on your Xcode development machine.

Configure the Azure Notification Hub

In the first post of this series about the implementation of Push Notifications in MAUI using Azure Notification Hub, I have created an Azure Notification Hub. From the Apple Developer website, I can download the Apple Push Notification service SSL Certificates for development and production.

Apple Push Notification service SSL Certificates - MAUI Push Notifications using Azure Notification Hub for iOS
Apple Push Notification service SSL Certificates

Now, go to the Azure portal and open the Notification Hub. Here I can upload the certificate that I created from the Apple Developer portal.

One important thing I learned and blocked me for more than 2 weeks is that I can import only one certificate. You see in the Application Mode, there are 2 options:

  • Production
  • Sandbox

What I did was to upload first the certificate for Production and Save and then the one for Sandbox and save. I was thinking that Azure saves both certificates but it was only my idea and I was wrong. Only one certificate is available. Now, I use the Sandbox and this is important because I have to configure the Entitlements.plist accordingly.

Add Entitlements.plist to the iOS Platform

To add a new entitlements file to your .NET MAUI app project, add a new XML file named Entitlements.plist to the Platforms\iOS folder of your app project. Then add the following XML to the file:

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
</dict>
</plist>

If I want to add the entitlement for push notifications, I have to add those lines

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
	<key>keychain-access-groups</key>
	<array>
		<string>$(AppIdentifierPrefix)yourappnamespace</string>
	</array>
	<key>aps-environment</key>
	<string>development</string>
</dict>
</plist>

The keychain-access-groups is the value that identifies your application. The aps-environment defines the environment for the push notification. It can have 2 values: development or production. This is explained in the Apple documentation. In the case of this post, I’m using development. When I deploy the application on the Apple Store, I should change it.

That has made me think that I have to create 2 Notification Hubs: one for development and one for production. Only Apple has 2 different settings. Windows and Android don’t distinguish between environments.

If you want to read more about the Entitlements.plist, there is a Microsoft documentation that explains every key.

Another thing it is important to know and I haven’t found anything about it is what Properties I have to set for the Entitlements.plist. There are no particular properties to set but I have to change the project properties as I describe in the next section.

Entitlements.plist properties
Entitlements.plist properties

Consume entitlements

A .NET MAUI iOS app must be configured to consume the entitlements defined in the Entitlements.plist file. So, in Visual Studio follow those steps:

  1. In Solution Explorer, right-click on your .NET MAUI app project and select Properties. Then, navigate to the iOS > Bundle Signing tab.
  2. In the Bundle Signing settings, click the Browse… button for the Custom Entitlements field.
  3. In the Custom Entitlements dialog, navigate to the folder containing your Entitlements.plist file, select the file, and click the Open button.
  4. In the project properties, the Custom Entitlements field will be populated with your entitlements file:

I think there is a bug in Visual Studio. Sometimes, when I set the Custom Entitlements and deploy on a device, I get some weird errors like

error MT1045: Failed to execute 'devicectl': 'devicectl -j /var/folders/dm/bwmxpbzn6bvdsyy73c_b453w0000gn/T/tmpBkAavG.tmp device install app --device "Enrico???s iPhone" /Users/enrico/Library/Caches/Xamarin/mtbs/builds/LanguageInUse/1fa03704bb15e35c6f47a701d9d92131e3e0740198296a93338bb3c829bc9cf7/bin/Debug/net8.0-ios/ios-arm64/device-builds/iphone15.2-17.3.1/LanguageInUse.app' returned the exit code 1. 0

After a few days, I saw that not only the Custom Entitlements but also the Custom Resource Rules had the same configuration but I haven’t set it. I deleted the Custom Resources Rules and I could deploy the application.

The code

Finally, I can go into my code to implement the Push Notification for iOS. First step is to ask the permissions to the user to send push notification.

Require user authorization

Now, open your AppDelegate.cs and in the FinishedLaunching add the request for the authorization. This will open a request from the system to the user if it wants to authorize your application to receive the notifications.

public override bool FinishedLaunching(UIApplication application, NSDictionary launchOptions)
{
    var result = base.FinishedLaunching(application, launchOptions);

    var authOptions = UNAuthorizationOptions.Alert | UNAuthorizationOptions.Badge | UNAuthorizationOptions.Sound;
    UNUserNotificationCenter.Current.RequestAuthorization(authOptions, (granted, error) =>
    {
        if (granted && error == null)
        {
            this.InvokeOnMainThread(() =>
            {
                UIApplication.SharedApplication.RegisterForRemoteNotifications();
                UNUserNotificationCenter.Current.Delegate = this;
            });
        }
    });

    return result;
}

If the user authorizes your app to receive the push notification, the function RegisterForRemoteNotifications is called. This is an important function that is coming from Apple.

Configure RegisterForRemoteNotifications

I can’t stress how much pain was to understand all the processes until here and how much time I spent in debugging without finding a reason why my code wasn’t working. I must use this function to set the push notification. First, I have to add and define this function in the code in this way

[Export("application:didRegisterForRemoteNotificationsWithDeviceToken:")]
public async void RegisteredForRemoteNotifications(UIApplication application, NSData deviceToken)
{
    // ...
}

In the deviceToken iOS gives to me what I have to use to set the push channel in the call to the Azure Notification Hub. This NSData is an Apple structure that contains an array of bytes. The problem was how to transform this sequence of bytes in a correct value to use. Fost forward to the solution, this is how to convert the deviceToken to the string I must use to register the device with the Azure Notification Hub.

string token = null!;
if (deviceToken.Length > 0)
{
    if (UIDevice.CurrentDevice.CheckSystemVersion(13, 0))
    {
        var data = deviceToken.ToArray();
        token = BitConverter
            .ToString(data)
            .Replace("-", "")
            .Replace("\"", "");
    }
    else if (!string.IsNullOrEmpty(deviceToken.Description))
    {
        token = deviceToken.Description.Trim('<', '>');
    }
}

It is important to check what version of the iOS operating system the user is currently on. If the version is a new one, I have to use BitConverter to get the string I need.

Now, I have to find the device Id. This is quite straightforward after spending a few hours googling and binging. This is the code

string deviceId = UIDevice.CurrentDevice.IdentifierForVendor.AsString();

Finally, I can call the Azure Notification Hub to register the device.

var deviceInstallation = new
{
    InstallationId = deviceId,
    Platform = "apns",
    PushChannel = token
};

using var httpClient = new HttpClient();
httpClient.DefaultRequestHeaders.Add("x-ms-version", "2015-01");
httpClient.DefaultRequestHeaders.TryAddWithoutValidation("Authorization",
                CreateToken($"https://{Constants.PushNotificationHubNamespace}.servicebus.windows.net",
                            "DefaultListenSharedAccessSignature",
                            Constants.PushNotificationSecret));
var t = await httpClient.PutAsJsonAsync($"https://{Constants.PushNotificationHubNamespace}.servicebus.windows.net/" +
                            $"{Constants.PushNotificationHub}/installations/{deviceInstallation.InstallationId}?api-version=2015-01",
                            deviceInstallation);

Receive the notification

Now, I have to tell iOS what has to happen when a message from push notification arrives. For that, there is a specific function called WillPresentNotification and my basic implementation is the following

[Export("userNotificationCenter:willPresentNotification:withCompletionHandler:")]
public void WillPresentNotification(UNUserNotificationCenter center, 
    UNNotification notification, 
    Action<UNNotificationPresentationOptions> completionHandler)
{
    var userInfo = notification.Request.Content.UserInfo;

    // tell the system to display the notification in a standard way
    // or use None to say app handled the notification locally
    completionHandler(UNNotificationPresentationOptions.Alert);
}

Wrap up

In conclusion, after a few long weeks, I finally managed to have my project working and receiving push notifications from Azure Notification Hub. I created those posts because I couldn’t find much documentation and the process, in particular for iOS, was very painful. Hopefully, this can help someone else avoid the mistakes I made. Please let me know in the comment below or the Forum if you have any questions, comments or suggestions.

If you want to support my job or just send a token of appreciation, you can consider paying me a beer using the Sponsor on GitHub.

Leave a Reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.