Adding Push Notifications to your iOS app with Swift and Twilio Notify

June 09, 2016
Written by
Sam Agnew
Twilion

Screen Shot 2016-06-06 at 5.08.06 AM

As of October 24, 2022, the Twilio Notify API is no longer for sale. Please refer to our Help Center Article for more information. 

Notify makes it easy to send cross-platform notifications to your users. From in-app push notifications on Android and iOS to SMS fallbacks, Notify takes care of the heavy lifting.

Let’s build an iOS app that can receive and react to push notifications sent by the Apple Push Notification Service (APNS) and Twilio Notify.

Tools and Agenda

There are a few things you will need in order to run all of the code in this tutorial:

To send push notifications we will need to:

  • Create a Twilio Credential and a provisioning profile for your Apple developer account
  • Configure a Node.js server to authenticate our iOS app
  • Create a Binding for Twilio to send notifications to your device
  • Write code to register your device with APNS
  • Write code to receive notifications from APNS
  • Bask in the glory of having a working app that you can modify to fit your needs

Configuring the server

We will build off the iOS Quickstart for Twilio Notify which uses Node.js on the server side to authenticate the iOS app.

Begin by cloning this repository for the Node server in your terminal:

git clone git@github.com:TwilioDevEd/notifications-quickstart-node.git

To send notifications on iOS, make sure you have your Apple push credentials configured with Twilio Notifications.

Follow this guide and make sure you have your certificate key and private key ready. Afterwards you should have a Twilio Credential of type APN with your certificate and private key as seen in this screenshot.

Screen Shot 2016-05-18 at 11.47.23 PM.png

Make sure “Use this credential for sending to a sandbox APN” is checked.

We need a Twilio Service Instance SID to configure our Node server. Head to Services, click Create Service and under APN Credential SID, choose the push credential you just created and hit Save.

Screen Shot 2016-05-18 at 11.49.58 PM.png

Next, grab your Account SID and Auth Token from your Twilio Console.

Open config.json and replace following variables with these values you just gathered:

  • TWILIO_ACCOUNT_SID
  • TWILIO_AUTH_TOKEN
  • TWILIO_CREDENTIAL_SID – This can be left blank, but is in the config file.
  • TWILIO_NOTIFICATION_SERVICE_SID

Now run the Node server by entering the following in your terminal in the Node quickstart directory:

npm start

 

Setting up the iOS app

We can now move on to setting up our iOS app. I have a barebones project already written that you can use to get started with a full storyboard.

Start by cloning this repository:

git clone git@github.com:sagnew/NotifyQuickstart.git

Next open the Xcode project:

open NotifySwiftQuickstart.xcodeproj

In order for push notifications to work, you’ll have to make sure the bundle identifier in Xcode matches the provisioning profile you made in your Apple developer account. I used the name NotifyQuickstart for mine, but you can use anything you want.

Screen Shot 2016-05-19 at 12.05.41 AM.png

Creating a Device Binding

We need to have a function that makes requests to the Node.js server in order to register the device. A “Binding” represents a unique device that can receive a notification.

In ViewController.swift replace the empty registerDevice(identity: String, deviceToken: String) function with the following:

func registerDevice(identity: String, deviceToken: String) {
    
    // Create a POST request to the /register endpoint with device variables to register for Twilio Notifications
    let session = NSURLSession.sharedSession()
    
    let url = NSURL(string: serverURL)
    let request = NSMutableURLRequest(URL: url!, cachePolicy: .UseProtocolCachePolicy, timeoutInterval: 30.0)
    request.HTTPMethod = "POST"
    
    request.addValue("application/json", forHTTPHeaderField: "Accept")
    request.addValue("application/json", forHTTPHeaderField: "Content-Type")
    
    let params = ["identity": identity,
                  "endpoint" : identity+deviceToken,
                  "BindingType" : "apn",
                  "Address" : deviceToken]
    
    let jsonData = try! NSJSONSerialization.dataWithJSONObject(params, options: [])
    request.HTTPBody = jsonData
    
    let requestBody = NSString(data: jsonData, encoding: NSUTF8StringEncoding)
    print("Request Body: \(requestBody)")
    
    let task = session.dataTaskWithRequest(request) { (responseData:NSData?, response:NSURLResponse?, error:NSError?) in
      if let responseData = responseData {
        let responseString = NSString(data: responseData, encoding: NSUTF8StringEncoding)
        print("Response Body: \(responseString)")
        do {
          let responseObject = try NSJSONSerialization.JSONObjectWithData(responseData, options: [])
          print("JSON: \(responseObject)")
        } catch let error {
          print("Error: \(error)")
        }
      }
    }
    task.resume()
  }

In this code we created an NSURLSession that will send an HTTP request to our Node server in order to register the device.

Now we need to call this function whenever the register button is tapped. Modify didTapRegister(sender: UIButton) to contain the following code:

@IBAction func didTapRegister(sender: UIButton) {
    if (TARGET_OS_SIMULATOR == 1) {
      displayError("Unfortunately, push notifications don't work on the Simulator")
    } else {
      let appDelegate = UIApplication.sharedApplication().delegate as! AppDelegate
      let deviceToken : String! = appDelegate.devToken
      let identity : String! = self.identityField.text
      registerDevice(identity, deviceToken: deviceToken)
      resignFirstResponder()
    }
  }

Now when the button is tapped, the device will be registered and Twilio will be able to send notifications to it.

Before running the app, we’ll need some code to register the device with APNS and to receive the notifications.

Registering a Device with APNS

The code for receiving notifications on the iOS app lives in your AppDelegate.swift.

We need to register the device with APNS, which will happen every time the app launches.

Open AppDelegate.swift and replace the empty application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) function with the following:

func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
    // Override point for customization after application launch.
    let settings = UIUserNotificationSettings(forTypes: .Alert, categories: nil)
    UIApplication.sharedApplication().registerUserNotificationSettings(settings)
    UIApplication.sharedApplication().registerForRemoteNotifications()
    return true
  }

This will cause iOS to prompt you to enable push notifications with a popup alert on the first launch.

If you want access to the notification, it will arrive in JSON format and contains the message body as a String that in the didReceiveRemoteNotification function.

In AppDelegate.swift again, replace ‘application(application: UIApplication, didReceiveRemoteNotification userInfo: [NSObject : AnyObject])’ with the following code to finish things off:

func application(application: UIApplication, didReceiveRemoteNotification userInfo: [NSObject : AnyObject]) {
    let message = userInfo["aps"]?["alert"] as? String
    let alertController = UIAlertController(title: "Notification", message: message, preferredStyle: .Alert)
    let defaultAction = UIAlertAction(title: "OK", style: .Default, handler: nil)
    alertController.addAction(defaultAction)
    self.window?.rootViewController?.presentViewController(alertController, animated: true, completion: nil)
  }

With this function we are ready to run the app and start receiving notifications.

Running the app and receiving a notification

Push notifications don’t work in the simulator so we need to run the app on a real device. This means that the Node server will be inaccessible to our iOS app as it is currently running on localhost.

We will use ngrok to make the Node server available outside of our development environment so that the mobile app can see it.

After installing ngrok, in a new terminal window run the following command and take a note of the forwarding URL generated.

$ ngrok http 3000

Now open ViewController.swift and for the serverURL variable, replace the placeholder value with your ngrok URL. Make sure to keep “/register” at the end of the URL as that will be the route on the Node server that our iOS app will send requests to.

Run the app on your device and you will get a prompt that asks if you want to receive notifications. Make sure you allow push notifications when this comes up. If you miss this prompt you can head to your settings, select “Notifications” and find the NotifySwiftQuickstart app to allow notifications.

You should see a simple UI where you can enter an identity for the user. When you click the button, you will create a Binding which uniquely identifies a user on a certain device, running your application.

Navigate back to the node quickstart in your terminal so we can send a notification to the app. The code for this app lives in the notify.js file. To send a notification to your iOS mobile app run this file, using the identifier you entered in the node app:

$ node notify.js YOUR_IDENTITY

You can see it in action here. I even locked my phone before sending the notification.

Notify.gif

Notifications at your fingertips

Push notifications are useful in anything from games to social media to alert users when new information is available and you are now equipped to send them with Twilio Notify in your iOS applications.

Let’s take a moment to celebrate:

TSquareBass

If you’re more of an Android fan you can check out this post my buddy Marcos wrote on push notifications for Android.

I can’t wait to see what you build, so contact me via the following channels and let me know how you’re using Twilio Notify: