Rich notifications with Vapor - Part 1: Building the backend
You will need Xcode 10+, Vapor 3+, Cocoapods, and some understanding of Vapor.
Introduction
In this tutorial we are going to build our backend service using Vapor. It will be responsible for sending a notification to our iOS application.
Sometimes sending notifications with only text just isn’t quite enough. This is where rich notifications come in. We can add images and videos to our notification to give the user a richer experience directly from their notification center.
Prerequisites
- A basic understanding of Vapor - please complete my “Getting started with Vapor” Part One and Part Two.
- Xcode 10+
- MacOS
- Vapor 3.0 - Install instructions here.
- An iOS device for testing notifications.
- An understanding of iOS development and Xcode environment.
- Cocoapods - Install instructions here.
- A REST client such as Postman and a basic understanding of how to use it.
Setting up our Vapor backend
Creating our Vapor project
From a terminal in your working directory enter the following command to create your Vapor application.
$ vapor new RichNotifyServer
$ cd RichNotifyServer
Now we will build your application before opening it in Xcode. Remember your first build could take some time to complete. Vapor update will make sure that you have the latest packages.
$ vapor build
$ vapor update
Now open your project in Xcode. Remember to open using Xcode you must run the following command in terminal:
$ vapor xcode -y
Also remember to change the run scheme to be the “run” scheme in case it is not already set to this.
Adding our dependency
We need to add the Pusher Beams package to our Swift Package Manager (SPM). Start by opening the Package.swift
file from the project navigator. This is our dependencies management file, a bit like our Podfile
or Cartfile
in iOS development.
Below the line that gives us our Vapor package dependency:
.package(url: "https://github.com/vapor/vapor.git", from: "3.0.0"),
Add the following to add our Pusher Beams dependency:
.package(url: "git@github.com:pusher/push-notifications-server-swift.git", .branch("master")),
We need to tell SPM what dependencies each target should have. We do this by editing targets section in the same file. Find the line:
.target(name: "App", dependencies: ["FluentSQLite", "Vapor"]),
Edit it so that it becomes:
.target(name: "App", dependencies: ["FluentSQLite", "Vapor", "PushNotifications"]),
We’ve added the name of our dependency to the array of dependencies for the app target. For more information on the dependency go here and for more information relating to SPM go here. Your Package.swift
file should now look something like this:
// Package.swift
// swift-tools-version:4.0
import PackageDescription
let package = Package(
name: "RichNotifyServer",
dependencies: [
// 💧 A server-side Swift web framework.
.package(url: "https://github.com/vapor/vapor.git", from: "3.0.0"),
.package(url: "git@github.com:pusher/push-notifications-server-swift.git", .branch("master")),
// 🔵 Swift ORM (queries, models, relations, etc) built on SQLite 3.
.package(url: "https://github.com/vapor/fluent-sqlite.git", from: "3.0.0")
],
targets: [
.target(name: "App", dependencies: ["FluentSQLite", "Vapor", "PushNotifications"]),
.target(name: "Run", dependencies: ["App"]),
.testTarget(name: "AppTests", dependencies: ["App"])
]
)
Now we need to close Xcode and install the package and reopen it. It’s best practice in Vapor to create and install files outside of our Xcode project using command line tools and then reopen Xcode using the toolbox command. After closing Xcode run the following command in the terminal in our working directory.
$ swift build
Reopen Xcode by running:
$ vapor xcode -y
Creating a Beams instance
Log in or create an account to access your dashboard here. Create a new Beams instance using the dashboard.
Complete step one of the iOS setup guide, we will pick up the remainder later on in this tutorial. Press the X to exit the setup guide and you will be returned to your dashboard for that instance. Scroll to the bottom of this page and you will find your Pusher Beams instance ID and secret key, make note of these you will need them later.
Sending notifications
Rich notifications
Rich notifications allow you to send different types of multi-media content. There are specific types of supported content that you can send. You can find up to date information on those content types here. You should also make note of the maximum size these files can be.
Creating our routes
We will be creating a simple application with one post request that we can use in order to send different push notifications using our REST client.
Open your routes.swift
file and replace the contents with the following:
//../Sources/App/routes.swift
import Vapor
import Vapor
// 1
import PushNotifications
/// Register your application's routes here.
public func routes(_ router: Router) throws {
// 2
router.post(PushContent.self, at: "push/") { req, data -> String in
PushNotificationService.send(content: data)
return "Push Success"
}
}
// 3
struct PushContent: Content {
let title: String
let message: String
let dataURL: String
}
//4
class PushNotificationService {
class func send(content: PushContent) throws {
let pushNotifications = PushNotifications(instanceId: "YOUR_INSTANCE_ID" , secretKey:"YOUR_SECRET_KEY")
let interests = ["general"]
let publishRequest = [
"apns": [
"aps": [
"alert": [
"title": content.title,
"body": content.message
],
"mutable-content": 1
],
"data": [
"attachment-url": content.dataURL
]
]
]
pushNotifications.publishToInterests(interests, publishRequest, completion: { publishID in
print("Published \(publishID)")
})
}
}
- We need to import the new PushNotifications dependency we have just installed.
- Here we create a new POST route that is capable of sending a specific push notification based on the data that is passed in using the wrapper class we have defined below.
- This is the structure that our post request is expecting the data to arrive in. This includes a string that contains the URL of the data that we want to show in our notification.
- This is our wrapper class. Here we create an instance of the dependency we have installed using the Beams instances ID and secret key you created earlier. We only have one interest in our app (which is general). We create a publish request using for APNS using the message that is passed in using the method call and we then publish this using our dependency. Make note of the
"mutable-content"
flag in the data structure, this will be intercepted by our notification service extension in iOS to display the data. Also make note of our"data"
key that includes our data url. Our application will use this information in order to download the image or video that we have sent.
Hit the run button in Xcode, you should see a message in the console saying it has started and is listening on http://localhost:8080. Leave this running in the background whilst you complete part two.
Conclusion
We’ve learnt how to add a dependency the Beams server side SDK as a dependency to Vapor and setup a basic route that can publish notifications to an iOS app that has registered for the interest. We’ve learnt about the different types of rich push notifications that can be sent and their limitations.
Now that you have a basic backend setup we need to go ahead and create the iOS application that can display these notifications. Stay tuned for part two!
The source code for this project can be found here.
14 March 2019
by Christopher Batin