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.
Sign up/Sign in
With a free Kodeco account you can download source code, track your progress, bookmark, personalise your learner profile and more!
Create accountAlready a member of Kodeco? Sign in
Sign up/Sign in
With a free Kodeco account you can download source code, track your progress, bookmark, personalise your learner profile and more!
Create accountAlready a member of Kodeco? Sign in
Contents
Sending Push Notifications With Vapor
30 mins
- Getting Started
- Understanding the Sample App’s Limitations
- Understanding and Configuring APNS
- APNS Device Registration Flow
- Creating the APNS Push Key
- Configuring the Server to Use the APNS Key
- Looking at the Device Model
- Using Push Channels
- Creating and Updating a Device With Routes
- Saving the Device Data From the iOS App
- Exposing the Local Server to the Web With Ngrok
- Running Ngrok
- Creating the First Device
- Updating the Device With the APNS Device Token
- Sending Push Notifications
- Targeting a Specific Device
- Sending a Notification to Channels
- Enriching the Notification
- Conforming to APNSwiftNotification
- Sending the Flight Notification to a Channel
- Testing the Flight Notification
- Where to Go From Here
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:
- 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.
- Initializes the
configuration
ofApplication
‘sapns
property by passing a JSON Web Token, also known as JWT, as its authentication method. - Passes the key information to create the JWT.
key
is the string declared in the first step, encoded asData
.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. - 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.
- 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.
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.
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.