Tutorial: Push notifications to Swift iOS apps that use the Notification Hubs REST API

In this tutorial, you use Azure Notification Hubs to push notifications to a Swift-based iOS application by using the REST API. You also create a blank iOS app that receives push notifications by using the Apple Push Notification service (APNs).

This tutorial takes you through the following steps:

  • Generate the certificate signing request file.
  • Request your app for push notifications.
  • Create a provisioning profile for the app.
  • Create a notification hub.
  • Configure the notification hub with APNs information.
  • Connect your iOS app to a notification hub.
  • Test the solution.

Prerequisites

To follow along, you need:

Even if you have no prior experience with iOS development, you should be able to follow along the steps for creating this first-principles example. However, you'll benefit from familiarity with the following concepts:

Note

The notification hub will be configured to use the Sandbox authentication mode only. You should not use this authentication mode for production workloads.

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. Select Keychain Access, expand Certificate Assistant, and then select Request a Certificate from a Certificate Authority.

    Use Keychain Access to request a new certificate

  3. 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.

    Required certificate information

  4. Enter a name for the CSR file in Save As, select the location in Where, and then select Save.

    Choose a file name for the certificate

    This action saves the CSR file in the selected location. The default location is Desktop. Remember the location chosen for the file.

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 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. After that, sign in with your Apple ID, select Identifiers, select App IDs, and finally select + to register a new app.

    iOS Provisioning Portal App IDs page

  2. Update the following three values for your new app, and then select Continue:

    • Name: Type a descriptive name for your app in the Name box in the App ID Description section.

    • Bundle Identifier: In the Explicit App ID section, enter a Bundle Identifier 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 will use the correct publishing profile.

    • Push Notifications: Check the Push Notifications option in the App Services section.

      Form to register a new App ID

      This action generates your App ID and requests that you confirm the information. Select Register to confirm the new App ID.

      After you select Register, you see the Registration complete screen as shown in the following image. Select Done.

      App ID registration complete showing entitlements

  3. In the Developer Center, under App IDs, locate the app ID that you created and select its row.

    App ID list

    Select the app ID to display the app details, and then select the Edit button at the bottom.

    Edit App ID page

  4. Scroll to the bottom of the screen and select the Create Certificate button under the Development Push SSL Certificate section.

    Create certificate for App ID button

    You now see the Add iOS Certificate assistant.

    Note

    This tutorial uses a development certificate. The same process is used when registering a production certificate. Just make sure that you use the same certificate type when sending notifications.

  5. Select Choose File, browse to the location where you saved the CSR file from the first task, and then select Generate.

    Generated certificate CSR upload page

  6. After the portal creates the certificate, select the Download button and then select Done.

    Generated certificate download page

    The certificate is downloaded and saved to your computer in your Downloads folder.

    Locate certificate file in the Downloads folder

    Note

    By default, the downloaded development certificate is named aps_development.cer.

  7. Select the downloaded push certificate aps_development.cer.

    This action installs the new certificate in the Keychain, as shown in the following image:

    Keychain access certificates list showing new certificate

    Note

    Although the name in your certificate might be different, the name will be prefixed with Apple Development iOS Push Services.

  8. 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.

    Export certificate as p12 format

    Make a note of the file name and location of the exported .p12 certificate. They are used to enable authentication with APNs.

    Note

    This tutorial creates a file named QuickStart.p12. Your file name and location might be different.

Create a provisioning profile for the app

  1. In the iOS Provisioning Portal, select Provisioning Profiles, select All, and then select + to create a new profile. You see the Add iOS Provisioning Profile wizard.

    Provisioning profile list

  2. Select iOS App Development under Development as the provisioning profile type, and select Continue.

  3. Next, select the app ID you created from the App ID drop-down list, and select Continue.

    Select the App ID

  4. In the Select certificates window, select your usual development certificate that you used for code signing, and select Continue. This certificate isn't the push certificate you created.

    Select the certificate

  5. Next, select the devices to use for testing, and select Continue.

    Select the devices

  6. Finally, pick a name for the profile in Profile Name, and select Generate.

    Choose a provisioning profile name

  7. When the new provisioning profile is created, choose to download and install it on your Xcode development machine. Then select Done.

    Download the provisioning profile

Create a notification hub

In this section, you create a notification hub and configure authentication with APNs by using the .p12 push certificate that you previously created. If you want to use a notification hub that you've already created, you can skip to step 5.

  1. Sign in to the Azure portal.

  2. Select All services on the left menu, and then select Notification Hubs in the Mobile section. Select the star icon next to the service name to add the service to the FAVORITES section on the left menu. After you add Notification Hubs to FAVORITES, select it on the left menu.

    Azure portal - select Notification Hubs

  3. On the Notification Hubs page, select Add on the toolbar.

    Notification Hubs - Add toolbar button

  4. On the Notification Hub page, do the following steps:

    1. Enter a name in Notification Hub.

    2. Enter a name in Create a new namespace. A namespace contains one or more hubs.

    3. Select a value from the Location drop-down list box. This value specifies the location in which you want to create the hub.

    4. Select an existing resource group in Resource Group, or create a name for a new resource group.

    5. Select Create.

      Azure portal - set notification hub properties

  5. Select Notifications (the bell icon), and then select Go to resource. You can also refresh the list on the Notification Hubs page and select your hub.

    Azure portal - notifications -> Go to resource

  6. Select Access Policies from the list. Note that the two connection strings are available to you. You'll need them later to handle push notifications.

    Important

    Do not use the DefaultFullSharedAccessSignature policy in your application. This is meant to be used in your back end only.

    Azure portal - notification hub connection strings

Configure your notification hub with APNs information

  1. Under Notification Services, select Apple (APNS).

  2. Select Certificate.

  3. Select the file icon.

  4. Select the .p12 file that you exported earlier.

  5. Specify the correct password.

  6. Select Sandbox mode. Use the Production mode only if you want to send push notifications to users who purchased your app from the store.

    Configure APNs certification in Azure portal

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

Connect your iOS app to a notification hub

In this section, you'll build the iOS app that will connect to the notification hub.

Create an iOS project

  1. In Xcode, create a new iOS project and select the Single View Application template.

  2. When setting the options for the new project:

    1. Specify the Product Name (PushDemo) and Organization Identifier (com.<organization>) that you used when you set Bundle Identifier in the Apple Developer Portal.

    2. Choose the Team that the App ID was set up for.

    3. Set the language to Swift.

    4. Select Next.

  3. Create a new folder called SupportingFiles.

  4. Create a new p-list file called devsettings.plist in the SupportingFiles folder. Be sure to add this folder to your gitignore file so it isn't committed when working with a git repo. In a production app, you would likely be conditionally setting these secrets as part of an automated build process. Such settings aren't covered in this walkthrough.

  5. Update devsettings.plist to include the following configuration entries by using your own values from the notification hub that you provisioned:

    Key Type Value
    notificationHubKey String <hubKey>
    notificationHubKeyName String <hubKeyName>
    notificationHubName String <hubName>
    notificationHubNamespace String <hubNamespace>

    You can find the required values by navigating to the notification hub resource in the Azure portal. In particular, the notificationHubName and notificationHubNamespace values are in the upper-right corner of the Essentials summary within the Overview page.

    Notification Hubs Essentials summary

    You can also find the notificationHubKeyName and notificationHubKey values by navigating to Access Policies and selecting the respective Access Policy, such as DefaultFullSharedAccessSignature. After that, copy from the Primary Connection String the value prefixed with SharedAccessKeyName= for notificationHubKeyName and the value prefixed with SharedAccessKey= for the notificationHubKey.

    The connection string should be in the following format:

    Endpoint=sb://<namespace>.servicebus.windows.net/;SharedAccessKeyName=<notificationHubKeyName>;SharedAccessKey=<notificationHubKey>
    

    To keep it simple, specify DefaultFullSharedAccessSignature so you can use the token to send notifications. In practice the DefaultListenSharedAccessSignature would be a better choice for situations where you only want to receive notifications.

  6. Under Project Navigator, select the Project Name and then select the General tab.

  7. Find Identity and then set the Bundle Identifier value so that it matches com.<organization>.PushDemo, which is the value used for the App ID from a previous step.

  8. Find Signing, and then select the appropriate Team for your Apple Developer Account. The Team value should match the one under which you created your certificates and profiles.

  9. Xcode should automatically pull down the appropriate Provisioning Profile value based on the Bundle Identifier. If you don't see the new Provisioning Profile value, try refreshing the profiles for the Signing Identity by selecting Xcode > Preferences > Account > View Details. Select Signing Identity, and then select the Refresh button in the bottom right to download the profiles.

  10. Select the Capabilities tab and ensure that push notifications are enabled.

  11. Open your AppDelegate.swift file to implement the UNUserNotificationCenterDelegate protocol and add the following code to the top of the class:

    @UIApplicationMain
    class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterDelegate {
    
        ...
    
        var configValues: NSDictionary?
        var notificationHubNamespace : String?
        var notificationHubName : String?
        var notificationHubKeyName : String?
        var notificationHubKey : String?
        let tags = ["12345"]
        let genericTemplate = PushTemplate(withBody: "{\"aps\":{\"alert\":\"$(message)\"}}")
    
        ...
    }
    

    You'll use these members later. Specifically, you'll use the tags and genericTemplate members as part of the registration. For more information on tags, see Tags for registrations and Template registrations.

  12. In the same file, add the following code to the didFinishLaunchingWithOptions function:

    if let path = Bundle.main.path(forResource: "devsettings", ofType: "plist") {
        if let configValues = NSDictionary(contentsOfFile: path) {
            self.notificationHubNamespace = configValues["notificationHubNamespace"] as? String
            self.notificationHubName = configValues["notificationHubName"] as? String
            self.notificationHubKeyName = configValues["notificationHubKeyName"] as? String
            self.notificationHubKey = configValues["notificationHubKey"] as? String
        }
    }
    
    if #available(iOS 10.0, *){
        UNUserNotificationCenter.current().delegate = self
        UNUserNotificationCenter.current().requestAuthorization(options: [.alert, .sound, .badge]) {
            (granted, error) in
    
            if (granted)
            {
                DispatchQueue.main.async {
                    application.registerForRemoteNotifications()
                }
            }
        }
    }
    
    return true
    

    This code retrieves the setting values from devsettings.plist, sets the AppDelegate class as the UNUserNotificationCenter delegate, requests authorization for push notifications, and then calls registerForRemoteNotifications.

    To keep it simple, the code supports iOS 10 and later only. You can add support for previous OS versions by conditionally using the respective APIs and approaches as you would normally do.

  13. In the same file, add the following functions:

    func application(_ application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data) {
        let installationId = (UIDevice.current.identifierForVendor?.description)!
        let pushChannel = deviceToken.reduce("", {$0 + String(format: "%02X", $1)})
    }
    
    func showAlert(withText text : String) {
        let alertController = UIAlertController(title: "PushDemo", message: text, preferredStyle: UIAlertControllerStyle.alert)
        alertController.addAction(UIAlertAction(title: "OK", style: UIAlertActionStyle.default,handler: nil))
        self.window?.rootViewController?.present(alertController, animated: true, completion: nil)
    }
    

    The code uses the installationId and pushChannel values to register with the notification hub. In this case, you're using UIDevice.current.identifierForVendor to provide a unique value to identify the device and then formatting the deviceToken to provide the desired pushChannel value. The showAlert function exists simply to display some message text for demonstration purposes.

  14. Still in the AppDelegate.swift file, add the willPresent and didReceive functions to UNUserNotificationCenterDelegate. These functions display an alert when they're notified that an app is running in either the foreground or the background respectively.

    @available(iOS 10.0, *)
    func userNotificationCenter(_ center: UNUserNotificationCenter, 
        willPresent notification: UNNotification, 
        withCompletionHandler completionHandler: @escaping (UNNotificationPresentationOptions) -> Void) {
        showAlert(withText: notification.request.content.body)
    }
    
    @available(iOS 10.0, *)
    func userNotificationCenter(_ center: UNUserNotificationCenter, 
        didReceive response: UNNotificationResponse, 
        withCompletionHandler completionHandler: @escaping () -> Void) {
        showAlert(withText: response.notification.request.content.body)
    }
    
  15. Add print statements to the bottom of the didRegisterForRemoteNotificationsWithDeviceToken function to verify that installationId and pushChannel are being assigned values.

  16. Create the Models, Services, and Utilities folders for the foundational components you'll be adding to the project later.

  17. Check that the project builds and runs on a physical device. Push notifications can't be tested by using the simulator.

Create models

In this step, you'll create a set of models to represent the Notification Hubs REST API payloads and to store the required shared access signature (SAS) token data.

  1. Add a new Swift file called PushTemplate.swift to the Models folder. This model provides a struct representing the BODY of an individual template as part of the DeviceInstallation payload.

    import Foundation
    
    struct PushTemplate : Codable {
        let body : String
    
        init(withBody body : String) {
            self.body = body
        }
    }
    
  2. Add a new Swift file called DeviceInstallation.swift to the Models folder. This file defines a struct representing the payload for creating or updating a Device Installation. Add the following code to the file:

    import Foundation
    
    struct DeviceInstallation : Codable {
        let installationId : String
        let pushChannel : String
        let platform : String = "apns"
        var tags : [String]
        var templates : Dictionary<String, PushTemplate>
    
        init(withInstallationId installationId : String, andPushChannel pushChannel : String) {
            self.installationId = installationId
            self.pushChannel = pushChannel
            self.tags = [String]()
            self.templates = Dictionary<String, PushTemplate>()
        }
    }
    
  3. Add a new Swift file called TokenData.swift to the Models folder. This model will be used to store a SAS token along with its expiration.

    import Foundation
    
    struct TokenData {
    
        let token : String
        let expiration : Int
    
        init(withToken token : String, andTokenExpiration expiration : Int) {
            self.token = token
            self.expiration = expiration
        }
    }
    

Generate a SAS token

Notification Hubs use the same security infrastructure as Azure Service Bus. To call the REST API, you'll need to programmatically generate a SAS token that can be used in the Authorization header of the request.

The resulting token will be in the following format:

SharedAccessSignature sig=<UrlEncodedSignature>&se=<ExpiryEpoch>&skn=<KeyName>&sr=<UrlEncodedResourceUri>

The process itself involves the same six key steps:

  1. Computing the expiry in UNIX Epoch time format, which means the number of seconds elapsed since midnight Coordinated Universal Time, January 1, 1970.
  2. Formatting the ResourceUrl that represents the resource you're trying to access so it's percent-encoded and lowercase. The ResourceUrl has the form 'https://<namespace>.servicebus.windows.net/<hubName>'.
  3. Preparing the StringToSign, which is formatted as '<UrlEncodedResourceUrl>\n<ExpiryEpoch>'.
  4. Computing and Base64-encoding the Signature by using the HMAC-SHA256 hash of the StringToSign value. The hash value is used with the Key part of the Connection String for the respective Authorization Rule.
  5. Formatting the Base64 encoded Signature so it's percent encoded.
  6. Constructing the token in the expected format by using the UrlEncodedSignature, ExpiryEpoch, KeyName, and UrlEncodedResourceUrl values.

See the Azure Service Bus documentation for a more thorough overview of shared access signature and how Azure Service Bus and Notification Hubs use it.

For the purposes of this Swift example, you're going to use Apple's open-source CommonCrypto library to help with the hashing of the signature. As it's a C library, it isn't accessible in Swift out of the box. You can make the library available by using a bridging header.

To add and configure the bridging header:

  1. In Xcode, select File > New > File > Header File. Name the header file BridgingHeader.h.

  2. Edit the file to import CommonHMAC.h:

    #import <CommonCrypto/CommonHMAC.h>
    
    #ifndef BridgingHeader_h
    #define BridgingHeader_h
    
    
    #endif /* BridgingHeader_h */
    
  3. Update the Target’s Build Settings to reference the bridging header:

    1. Open the Building Settings tab and scroll down to the Swift Compiler section.

    2. Ensure that the Install Objective-C Compatibility Header option is set to Yes.

    3. Enter the file path '<ProjectName>/BridgingHeader.h' into the Objective-C bridging Header option. This is the file path to our bridging header.

    If you can't find these options, ensure that you have the All view selected rather than Basic or Customized.

    There are many third-party open-source wrapper libraries available that might make using CommonCrypto a bit easier. However, discussion of such libraries is beyond the scope of this article.

  4. Add a new Swift file named TokenUtility.swift within the Utilities folder and add the following code:

    import Foundation
    
    struct TokenUtility {    
         typealias Context = UnsafeMutablePointer<CCHmacContext>
    
         static func getSasToken(forResourceUrl resourceUrl : String, withKeyName keyName : String, andKey key : String, andExpiryInSeconds expiryInSeconds : Int = 3600) -> TokenData {
             let expiry = (Int(NSDate().timeIntervalSince1970) + expiryInSeconds).description
             let encodedUrl = urlEncodedString(withString: resourceUrl)
             let stringToSign = "\(encodedUrl)\n\(expiry)"
             let hashValue = sha256HMac(withData: stringToSign.data(using: .utf8)!, andKey: key.data(using: .utf8)!)
             let signature = hashValue.base64EncodedString(options: .init(rawValue: 0))
             let encodedSignature = urlEncodedString(withString: signature)
             let sasToken = "SharedAccessSignature sr=\(encodedUrl)&sig=\(encodedSignature)&se=\(expiry)&skn=\(keyName)"
             let tokenData = TokenData(withToken: sasToken, andTokenExpiration: expiryInSeconds)
    
             return tokenData
         }
    
         private static func sha256HMac(withData data : Data, andKey key : Data) -> Data {
             let context = Context.allocate(capacity: 1)
             CCHmacInit(context, CCHmacAlgorithm(kCCHmacAlgSHA256), (key as NSData).bytes, size_t((key as NSData).length))
             CCHmacUpdate(context, (data as NSData).bytes, (data as NSData).length)
             var hmac = Array<UInt8>(repeating: 0, count: Int(CC_SHA256_DIGEST_LENGTH))
             CCHmacFinal(context, &hmac)
    
             let result = NSData(bytes: hmac, length: hmac.count)
             context.deallocate()
    
             return result as Data
         }
    
         private static func urlEncodedString(withString stringToConvert : String) -> String {
             var encodedString = ""
             let sourceUtf8 = (stringToConvert as NSString).utf8String
             let length = strlen(sourceUtf8)
    
             let charArray: [Character] = [ ".", "-", "_", "~", "a", "z", "A", "Z", "0", "9"]
             let asUInt8Array = String(charArray).utf8.map{ Int8($0) }
    
             for i in 0..<length {
                 let currentChar = sourceUtf8![i]
    
                 if (currentChar == asUInt8Array[0] || currentChar == asUInt8Array[1] || currentChar == asUInt8Array[2] || currentChar == asUInt8Array[3] ||
                     (currentChar >= asUInt8Array[4] && currentChar <= asUInt8Array[5]) ||
                     (currentChar >= asUInt8Array[6] && currentChar <= asUInt8Array[7]) ||
                     (currentChar >= asUInt8Array[8] && currentChar <= asUInt8Array[9])) {
                     encodedString += String(format:"%c", currentChar)
                 }
                 else {
                     encodedString += String(format:"%%%02x", currentChar)
                 }
             }
    
             return encodedString
         }
     }
    

    This utility encapsulates the logic responsible for generating the SAS token.

    As outlined previously, the getSasToken function orchestrates the high-level steps required to prepare the token. The function will be called by the installation service later in this tutorial.

    The other two functions are called by the getSasToken function: sha256HMac for computing the signature and urlEncodedString for encoding the associated URL string. The urlEncodedString function is required as it isn't possible to achieve the required output by using the built-in addingPercentEncoding function.

    The Azure Storage iOS SDK is an excellent example of how to approach these operations in Objective-C. Further information on Azure Service Bus SAS tokens can be found in the Azure Service Bus documentation.

Verify the SAS token

Before you implement the installation service in the client, check that our app is correctly generating the SAS token by using your HTTP utility of choice. For the purposes of this tutorial, our tool of choice will be Postman.

Use an appropriately placed print statement or breakpoint to note the installationId and the token values being generated by the app.

Follow these steps to call the installations API:

  1. In Postman, open a new tab.

  2. Set the request to GET and specify the following address:

    https://<namespace>.servicebus.windows.net/<hubName>/installations/<installationId>?api-version=2015-01
    
  3. Configure the request headers as follows:

    Key Value
    Content-Type application/json
    Authorization <sasToken>
    x-ms-version 2015-01
  4. Select the Code button that appears on the upper-right under the Save button. The request should look similar to the following example:

    GET /<hubName>/installations/<installationId>?api-version=2015-01 HTTP/1.1
    Host: <namespace>.servicebus.windows.net
    Content-Type: application/json
    Authorization: <sasToken>
    x-ms-version: 2015-01
    Cache-Control: no-cache
    Postman-Token: <postmanToken>
    
  5. Select the Send button.

No registration exists for the specified installationId at this point. The verification should result in a "404 Not Found" response rather than a "401 Unauthorized" response. This result should confirm that the SAS token has been accepted.

Implement the installation service class

Next you'll implement our basic wrapper around the Installations REST API.

Add a new Swift file called NotificationRegistrationService.swift under the Services folder, and then add the following code to this file:

import Foundation

class NotificationRegistrationService {
    private let tokenizedBaseAddress: String = "https://%@.servicebus.windows.net/%@"
    private let tokenizedCreateOrUpdateInstallationRequest = "/installations/%@?api-version=%@"
    private let session = URLSession(configuration: URLSessionConfiguration.default)
    private let apiVersion = "2015-01"
    private let jsonEncoder = JSONEncoder()
    private let defaultHeaders: [String : String]
    private let installationId : String
    private let pushChannel : String
    private let hubNamespace : String
    private let hubName : String
    private let keyName : String
    private let key : String
    private var tokenData : TokenData? = nil

    init(withInstallationId installationId : String,
            andPushChannel pushChannel : String,
            andHubNamespace hubNamespace : String,
            andHubName hubName : String,
            andKeyName keyName : String,
            andKey key: String) {
        self.installationId = installationId
        self.pushChannel = pushChannel
        self.hubNamespace = hubNamespace
        self.hubName = hubName
        self.keyName = keyName
        self.key = key
        self.defaultHeaders = ["Content-Type": "application/json", "x-ms-version": apiVersion]
    }

    func register(
        withTags tags : [String]? = nil,
        andTemplates templates : Dictionary<String, PushTemplate>? = nil,
        completeWith completion: ((_ result: Bool) -> ())? = nil) {

        var deviceInstallation = DeviceInstallation(withInstallationId: installationId, andPushChannel: pushChannel)

        if let tags = tags {
            deviceInstallation.tags = tags
        }

        if let templates = templates {
            deviceInstallation.templates = templates
        }

        if let deviceInstallationJson = encodeToJson(deviceInstallation) {
            let sasToken = getSasToken()
            let requestUrl = String.init(format: tokenizedCreateOrUpdateInstallationRequest, installationId, apiVersion)
            let apiEndpoint = "\(getBaseAddress())\(requestUrl)"

            var request = URLRequest(url: URL(string: apiEndpoint)!)
            request.httpMethod = "PUT"

            for (key,value) in self.defaultHeaders {
                request.addValue(value, forHTTPHeaderField: key)
            }

            request.addValue(sasToken, forHTTPHeaderField: "Authorization")
            request.httpBody = Data(deviceInstallationJson.utf8)

            (self.session.dataTask(with: request) { dat, res, err in
                if let completion = completion {
                        completion(err == nil && (res as! HTTPURLResponse).statusCode == 200)
                }
            }).resume()
        }
    }

    private func getBaseAddress() -> String {
        return String.init(format: tokenizedBaseAddress, hubNamespace, hubName)
    }

    private func getSasToken() -> String {
        if (tokenData == nil ||
            Date(timeIntervalSince1970: Double((tokenData?.expiration)!)) < Date(timeIntervalSinceNow: -(5 * 60))) {
            self.tokenData = TokenUtility.getSasToken(forResourceUrl: getBaseAddress(), withKeyName: self.keyName, andKey: self.key)
        }

        return (tokenData?.token)!
    }

    private func encodeToJson<T : Encodable>(_ object: T) -> String? {
        do {
            let jsonData = try jsonEncoder.encode(object)
            if let jsonString = String(data: jsonData, encoding: .utf8) {
                return jsonString
            } else {
                return nil
            }
        }
        catch {
            return nil
        }
    }
}

The requisite details are provided as part of initialization. Tags and templates are optionally passed into the register function to form part of the Device Installation JSON payload.

The register function calls the other private functions to prepare the request. After a response is received, the completion is called and indicates whether the registration was successful.

The request endpoint is constructed by the getBaseAddress function. The construction uses the notification hub parameters namespace and name that were provided during initialization.

The getSasToken function checks whether the currently stored token is valid. If the token isn't valid, the function calls TokenUtility to generate a new token and then stores it before returning a value.

Finally, encodeToJson converts the respective model objects into JSON for use as part of the request body.

Invoke the Notification Hubs REST API

The last step is updating AppDelegate to use NotificationRegistrationService to register with our NotificationHub.

  1. Open AppDelegate.swift and add a class-level variable to store a reference to the NotificationRegistrationService:

    var registrationService : NotificationRegistrationService?
    
  2. In the same file, update the didRegisterForRemoteNotificationsWithDeviceToken function to initialize the NotificationRegistrationService with the requisite parameters, and then call the register function.

    func application(_ application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data) {
        let installationId = (UIDevice.current.identifierForVendor?.description)!
        let pushChannel = deviceToken.reduce("", {$0 + String(format: "%02X", $1)})
    
        // Initialize the Notification Registration Service
        self.registrationService = NotificationRegistrationService(
            withInstallationId: installationId,
            andPushChannel: pushChannel,
            andHubNamespace: notificationHubNamespace!,
            andHubName: notificationHubName!,
            andKeyName: notificationHubKeyName!,
            andKey: notificationHubKey!)
    
        // Call register, passing in the tags and template parameters
        self.registrationService!.register(withTags: tags, andTemplates: ["genericTemplate" : self.genericTemplate]) { (result) in
            if !result {
                print("Registration issue")
            } else {
                print("Registered")
            }
        }
    }
    

    To keep it simple, you're going to use a couple of print statements to update the output window with the result of the register operation.

  3. Now build and run the app on a physical device. You should see "Registered" in the output window.

Test the solution

Our app at this stage is registered with NotificationHub and can receive push notifications. In Xcode, stop the debugger and close the app if it's currently running. Next, check that the Device Installation details are as expected and that our app can now receive push notifications.

Verify the device installation

You can now make the same request as you did previously by using Postman for verifying the SAS token. Assuming that the SAS token hasn't expired, the response should now include the installation details you provided, such as the templates and tags.

{
    "installationId": "<installationId>",
    "pushChannel": "<pushChannel>",
    "pushChannelExpired": false,
    "platform": "apns",
    "expirationTime": "9999-12-31T23:59:59.9999999Z",
    "tags": [
        "12345"
    ],
    "templates": {
        "genericTemplate": {
            "body": "{\"aps\":{\"alert\":\"$(message)\"}}",
            "tags": [
                "genericTemplate"
            ]
        }
    }
}

Send a test notification (Azure portal)

The quickest way to test that you can now receive notifications is to browse to the notification hub in the Azure portal:

  1. In the Azure portal, browse to the Overview tab on your notification hub.

  2. Select Test Send, which is above the Essentials summary in the upper-left of the portal window:

    Notification Hubs Essentials Summary Test Send Button

  3. Choose Custom Template from the Platforms list.

  4. Enter 12345 for the Send to Tag Expression. You had previously specified this tag in our installation.

  5. Optionally edit the message in the JSON payload:

    Notification Hubs Test Send

  6. Select Send. The portal should indicate whether the notification was successfully sent to the device:

    Notification Hubs Test Send Results

    Assuming that the app isn't running in the foreground, you should also see a notification in the Notification Center on your device. Tapping the notification should open the app and show the alert.

    Notification Received Example

Send a test notification (Mail carrier)

You can send notifications via the REST API by using Postman, which may be a more convenient way to test.

  1. Open a new tab in Postman.

  2. Set the request to POST, and enter the following address:

    https://<namespace>.servicebus.windows.net/<hubName>/messages/?api-version=2015-01
    
  3. Configure the request headers as follows:

    Key Value
    Content-Type application/json;charset=utf-8
    Authorization <sasToken>
    ServiceBusNotification-Format template
    Tags "12345"
  4. Configure the request BODY to use RAW - JSON (application.json) with the following JSON payload:

    {
       "message" : "Hello from Postman!"
    }
    
  5. Select the Code button, which is under the Save button on the upper-right of the window. The request should look similar to the following example:

    POST /<hubName>/messages/?api-version=2015-01 HTTP/1.1
    Host: <namespace>.servicebus.windows.net
    Content-Type: application/json;charset=utf-8.
    ServiceBusNotification-Format: template
    Tags: "12345"
    Authorization: <sasToken>
    Cache-Control: no-cache
    Postman-Token: <postmanToken>
    
    {
        "message" : "Hello from Postman!"
    }
    
  6. Select the Send button.

You should get a success status code and receive the notification on the client device.

Next steps

You now have a basic iOS Swift app connected to a notification hub via the REST API and can send and receive notifications. For more information, see the following articles: