Sending Push Notifications With Vapor

Adding push notifications to your app enriches the user experience by delivering data to their devices when it becomes available. In this article, you’ll learn how to send push notifications with Vapor. By Natan Rolnik.

5 (9) · 1 Review

Download materials
Save for later
Share
You are currently viewing page 2 of 4 of this article. Click here to view the first page.

Configuring the Server to Use the APNS Key

In this section, you’ll implement configurePush() by configuring Vapor’s APNS package. This method handles transforming the key and the JSON payloads to the format that APNS expects.

Back in the Server project, open Application+APNS.swift from the Extensions group. Add the following code inside configurePush():

// 1
let appleECP8PrivateKey =
"""
-----BEGIN PRIVATE KEY-----

-----END PRIVATE KEY-----
"""

// 2
apns.configuration = try .init(
    authenticationMethod: .jwt(
        // 3
        key: .private(pem: Data(appleECP8PrivateKey.utf8)),
        keyIdentifier: "<#Key identifier#>",
        teamIdentifier: "<#Team identifier#>"
    ),
    // 4
    topic: "com.raywenderlich.airplanespotter",
    // 5
    environment: .sandbox
)

This is what the code above does:

  1. Declares a string using a multi-line string literal, which contains the contents of the APNS key. Be sure to replace the contents with the actual content of your key.
  2. Initializes the configuration of Application‘s apns property by passing a JSON Web Token, also known as JWT, as its authentication method.
  3. Passes the key information to create the JWT. key is the string declared in the first step, encoded as Data. keyIdentifier is the ten-character-long string present in the key’s file name. teamIdentifier is your Apple Developer Team ID, which you can find in the Membership page in the Developer Portal.
  4. Sets the iOS application’s bundle identifier as the topic parameter. This is the bundle identifier used in the sample iOS app; if you changed it, you must make the same change here.
  5. Finally, it sets the environment that APNS will use to send the notifications: in this case, .sandbox. When releasing to the AppStore, or via TestFlight and AdHoc, set it to .production.

Build and run. If you configured everything correctly, you won’t see any errors.

Now your server is ready to send authenticated requests to APNS. The next piece in the puzzle is getting a device token to send a notification to.

Looking at the Device Model

The sample server app isn’t aware of users; it only knows devices. Every Device row in the devices table represents a unique installation of your app.

Even if your server allows user registration, keeping devices in a separate table and linking them with a children-parent relationship is a better approach. A user might log in with multiple devices, and having a separate table allows the server to save each one’s data independently.

The sample app tracks each device’s operating system and version. In your own app, you might want to save the app version, the time zone, last launch time or other properties.

Additionally, the Device model has a channels property, which allows sending notifications based on subscription segments.

Using Push Channels

Although this tutorial will teach you how to send notifications to a specific device, it also covers a handy way for sending pushes to a group of users. Push channels are useful when you want to create a publisher-subscriber model for sending notifications.

For example, an app that displays soccer results and news could send pushes to the Barcelona-goals channel whenever Barcelona scores or concedes a goal, to Barcelona-news when news about the team is published and to Barcelona-all for the users who don’t want to miss anything!

You can achieve this with Fluent’s Siblings properties, which in turn use pivot tables to link between multiple rows of two tables.

Note: In this tutorial, for the sake of simplicity, the channels property is an array of strings stored as a single string joined by line breaks. For faster queries about which devices are subscribed to one or more channels, you should use a many-to-many relationship.

You can achieve this with Fluent’s Siblings properties, which in turn use pivot tables to link between multiple rows of two tables.

Creating and Updating a Device With Routes

Open DeviceController.swift. Inside boot(routes:), you’ll notice that two routes are declared under the devices group.

The first route is devices/update, which forwards creating the device in the server’s database, or updating its fields, to put(_:).

Scroll to put(_:)‘s implementation and you’ll find that the server expects an UpdateDevice in the request body. If this is an existing device, its id will be present in the payload, and the server can find it in the database to update its properties.

Otherwise, if this is a new device, the server will create a new row in the database and return it in the response. This method returns an EventLoopFuture so it can set the correct HTTP status depending on whether the device is new (HTTP 201, created) or existing (HTTP 200, OK).

The controller also contains a second route, devices/:id/test-push. However, its implementation in sendTestPush(_ req:) returns an error at the moment. You’ll configure it to resolve this error later.

Saving the Device Data From the iOS App

Now, it’s time to run the server and test that the iOS app can call the server. This will allow the server to save the device, then update it once APNS provides the device token.

Exposing the Local Server to the Web With Ngrok

In development, the server runs on your own machine and is reachable via localhost, or 127.0.0.1. Because the iOS Simulator doesn’t support push tokens, you’ll need to run the app on an iOS device: an iPhone or an iPad.

As a consequence, there are two ways of making the local server available to the iOS device. The first, and slightly more complicated, is by finding the IP of your computer and setting it as the server host in the iOS app. The second is using a tool like ngrok to expose your local server to the public internet, then use the URL it provides upon login. This tutorial will guide you through the second option.

For server developers, especially those who are also mobile developers, ngrok makes testing your APIs much simpler. Create an account if you don’t have one, then follow the download and setup instructions.

Note: If you want to call ngrok from any folder in your computer in Terminal, move it to /usr/local/bin. Otherwise, make sure your Terminal session is in the same directory where you placed the ngrok binary.

If the server isn’t running, open the server project in Xcode and build and run it. You’ll see the following message in the console:

[ NOTICE ] Server starting on http://127.0.0.1:8080

This means the server is up and accessible in your machine via the 8080 port.